web component
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引入。
storybook集成
storybook如果直接接入web component会导致无法正常工作,因此在init的时候应当选择html模板。
package.json添加script命令
build:watch
,设置成stencil build --watch
,这样就可以在编写代码的时候自动重新编译。然后在.storybook/preview.js中添加
1 import { defineCustomElements } from '../dist/esm/loader';
2 defineCustomElements();