文章

闭包

什么是闭包

闭包是词法作用域和执行栈下的自然产物。在JavaScript中有全局作用域、函数作用域、块级作用域(ES6),

词法作用域取决于书写的位置,对于函数作用域而言,它能够访问更外层的作用域中的变量,但是外部作用域却无法访问函数作用域中的变量,当一个函数能够访问另一个函数作用域中的变量时就产生了闭包,这通常是一个嵌套函数。

内存泄漏

闭包的缺点是容易导致内存泄漏,这是因为执行栈机制,执行栈是一种管理执行上下文的机制,在JavaScript中有全局执行上下文、函数执行上下文和eval执行上下文这三种执行上下文。对于闭包而言,当外层函数执行后就会将该函数压入执行栈中,而当执行内层函数时会将内层函数的执行上下文压入栈中,等内层函数执行完毕则依次出栈内层函数和外层函数,但是假如有变量一直保持着对内层函数的引用,此时内层函数就不会被释放,而由于执行栈的先进后出的规则,就会导致外层函数执行上下文也不会被释放。

// 执行完毕就出栈释放
function a(){
function b(){}
b()
}
a()
// 一直保持内层函数的引用
function a(){
return function b(){}
}
const c = a()

闭包的作用

  1. 封装
  2. 作用域的扩展(可以在外部作用域中访问函数作用域内的变量)

this

web component

web component 的目的是实现可复用的定制元素。web component与vue这类MVVM框架的组件非常相似,但是不同的地方在于,web component是原生支持的,浏览器可以识别它,不需要经过编译才能被浏览器识别。

web component有以下特点

  • 原生支持,性能更好,迭代前景更好
  • 无关框架,理论上任何基于js的框架都能使用
  • 基于shadow DOM(影子DOM),保证组件功能和样式的私有性,实现真正的组件化

web component由三大部分组成

  1. 自定义元素。 可以在html使用自定义的标签,来实现UI。
  2. 影子DOM。创建影子DOM树,并附加到主文档DOM上,并保证内部功能和样式的私有话,防止内部与外部功能和样式冲突。
  3. HTML模板,使用template slot ,使得元素结构能够被复用和定制。

stenciljs

stencil是ionic团队开发的一款用于开发基于web component的系统或组件的编译器。

优点

  • 大量使用装饰器,学习成本低,尤其是如果有Vue这类MVVM框架的经验时,很容易上手
  • 自动生成使用文档
  • 原生支持typescript和tsx
  • 提供多种框架集成的案例
  • 基于rollup打包,比webpack速度快
  • 集成e2e测试

最佳实践

尽量减少DOM结构

减少DOM结构不仅能够提升性能,更能使用户更方便地自定义样式。

穿透样式

  1. 当DOM结构比较简单使,使用inherit来继承Host上的样式
  2. 提供css变量来定制关键样式
  3. 尽可能地提供part,利用::part伪类元素选择器来定制样式(注意:part选择器无法选中子孙)

快速开发 vs 控制反转

对于不复杂且通用的组件逻辑,提供Prop来控制,对于复杂的组件功能,提供slot来实现。换句话说就是通过Prop来实现快速开发,通过slot(控制反转思想的实现)来提供定制组件的能力。

保留一个_this指向节点

component有时候会自行调用某些方法,例如message组件会在1.5s后调用destroy方法自动销毁,而这个时候就需要访问到对应到元素节点,这里有两种解决方法,一种是为元素节点设置一个id,并且每创建一个节点就让id++,并将id保存到this中,这样就可以通过document.querySelector('特定前缀' + this.id)来访问真实节点;另一种方法是通过jsx ref,直接获取host到节点对象,并存储为this的一个属性上(_this)。

第二种方法比第一种好,因为第二种不需要为host设置id,避免覆盖了用户设置的id。

更新:stencil提供@Element装饰器,可以直接获取Host元素。

踩坑记录

this的指向

stencil采用class来创建组件,而在class中this的指向有多种情况。

通常情况下this是指向实例,但是当类方法是作为事件处理程序被调用时,this则指向当前节点。

解决方法是使用箭头函数或者bind绑定this为类实例。

@font-face

在影子DOM使用@font-face无法正常的加载字体,无论是本地还是网络加载。目前的解决方式是通过js在主文档中创建style标签进行加载,或者直接将css引入。