一、什么是Web Components?
Web Components是一个浏览器的新功能,它允许开发者创建可重用的定制元素,该定制元素的功能封装在代码之外,定制元素的功能部分由自身的html结构、css样式以及javascript代码组成,并且不会干扰到代码中其他元素,该元素被定义之后可以在任何web应用中使用,使用方式同原生标签元素。它不依赖于任何框架,可实现跨框架使用,这也解决了项目重构时换框架即需要重新开发组件的痛点。
浏览器兼容性可参考:Web Components的浏览器兼容性
二、为什么使用Web Components?
相比于其他组件,使用Web Components技术开发的组件拥有以下几个显著优点:
1、一劳永逸
组件拥有更长的寿命,它不需要适应新的技术而重写。虽然我们团队目前的主要技术栈是vue,但是也要考虑到将来项目重构时会面临技术栈升级或者更换的一个情况。
2、语法简单易学
组件仅由html、css、javascript三部分组成,使用它可以不像使用依赖库或者框架的组件一样去额外学习一些框架的特定语言。
3、可移植性强
组件可以在任何web应用中使用,因为很少甚至没有依赖,组件的使用障碍要明显低于依赖库或者框架的组件。
但同时,Web Components还存在一些弊端,由于目前浏览器支持性还不太高,而且官方教程写的也不是非常清晰易懂,使用起来还是有一定难度的。
由于我们团队开发的项目的只需要兼容google浏览器,而且团队技术栈又比较广泛,涉及vue、react、angluar,考虑到业务组件的可移植性,我选择使用Web components。
三、怎么使用Web Components?
使用部分的介绍以vue框架下我开发的一个业务组件为例,该组件主要实现两个功能:1、在浏览器展示一段文字;2、所展示的这段文字中的关键字要标红。接下来正式进入使用步骤的介绍。
2.1 使用customElements.define()创建一个自定义元素
window.customElements 是 CustomElementRegistry 的实例,CustomElementRegistry提供注册自定义元素和查询已注册元素的方法。
customElements.define()在创建一个类后定义自定义元素,它接受三个参数:
- 第一个参数为所创建元素的名称 (注:为了和原生的元素区分开,元素的名称不能是单个单词,且其中必须要有短横线,eg: emphasize-words)
- 第二个参数为定义元素行为的类
- 第三个参数为可选参数,是一个包含extends属性的配置对象,它指定所创建的元素继承自哪个内置元素,可以继承任何内置元素
(function() {
let target_template = document.createElement("template");
target_template.setAttribute("id", "emphasizeWordsTemplate");
//引号内填写的HTML代码
target_template.innerHTML = `
`;
document.body.append(target_template);
// 将父组件传来的纯文本变成带样式(将关键词标红)的html
class EmphasizeWords extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow( { mode: 'closed' } );
var templateElem = document.getElementById('emphasizeWordsTemplate');
var content = templateElem.content.cloneNode(true);
// 文本
let text = this.getAttribute('text');
// 关键词
let keyWords = String(this.getAttribute('keyWords')).split(',');
// 标记颜色
let color = this.getAttribute('markColor');
// 将文本中关键词替换
for (let i = 0; i < keyWords.length; i++) {
let r = new RegExp(keyWords[i], "ig");
if(r.test(text)) {
text = text.replace(r, `${keyWords[i]}`)
}
}
content.getElementById('words').innerHTML = text
shadow.appendChild(content);
}
}
window.customElements.define('emphasize-words', EmphasizeWords);
})();
2.2 引入自定义元素
Web components有两种引入方式,以vue框架下引入方式为例:
方法1、在vue的主渲染文件index.html使用script
标签将刚刚创建的Web components的js文件引入
方法2、在vue的入口文件main.js或某个独立vue组件中使用ES module的形式引入
import './emphasize-words'
2.3 使用Web components
Web components通过向浏览器注册自定义元素实现,注册后的自定义元素,使用方法上和原生标签元素相比没有什么区别,你只需要传入自定义元素要求的属性即可,例如:
在vue、react等框架中使用则需要遵守框架特定的语法,例如在vue中你可以这样使用它:
2.4 用生命周期解决自定义元素使用时报错问题
如上2.1的示例可能会产生一个问题——若这个组件还未加载完就被调用,会导致组件报错。这时,就要用到生命周期函数。
在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:
connectedCallback:当 custom element首次被插入文档DOM时,被调用。
disconnectedCallback:当 custom element从文档DOM中删除时,被调用。
adoptedCallback:当 custom element被移动到新的文档时,被调用。
attributeChangedCallback: 当 custom element增加、删除、修改自身属性时,被调用。
将上述功能代码的调用位置稍作修改即可(等组件加载完毕再执行功能代码)
let target_template = document.createElement("template");
target_template.setAttribute("id", "emphasizeWordsTemplate");
//引号内填写的HTML代码
target_template.innerHTML = `
`;
document.body.append(target_template);
class EmphasizeWords extends HTMLElement {
constructor() {
super();
}
init(){
let shadow = this.attachShadow( { mode: 'open' } );
let templateElem = document.getElementById('emphasizeWordsTemplate');
let content = templateElem.content.cloneNode(true);
// 将父组件传来的文本变成带样式的的html
console.log(this, this.getAttribute('text'));
// 文本
let text = this.getAttribute('text');
console.log(text);
// 关键词
let keyWords = String(this.getAttribute('keyWords')).split(',');
// 标记颜色
let color = this.getAttribute('markColor');
// 将文本中关键词替换
for (let i = 0; i < keyWords.length; i++) {
let r = new RegExp(keyWords[i], "ig");
if(r.test(text)) {
text = text?.replace(r, `${keyWords[i]}`)
}
}
content.getElementById('words').innerHTML = text
shadow.appendChild(content);
}
connectedCallback() {
console.log('被加载')
this.init();
}
disconnectedCallback() {
console.log('被删除')
}
adoptedCallback() {
console.log('被移动到新的文档')
}
attributeChangedCallback() {
console.log('增加、删除、修改自身属性')
}
}
window.customElements.define('emphasize-words', EmphasizeWords);
四、展示结果
浏览器展示结果:
控制台打印传值后的emphasize-words标签及其text属性内容:
五、总结
Web components 的出现让组件可以快速适应变化,让组件拥有更长的寿命,但其浏览器的支持性不高的缺点也使它目前没有成为主流的选择。本篇文章只简单介绍了Web components在vue项目中的使用,关于Web components的知识点还有非常多,比如插槽的使用、现有的Web components库等,之后也会根据使用和调研情况慢慢完善该系列文章。
六、参考材料
- Web Components 官方文档
- Facebook React 和 Web Components(Polymer)对比优势和劣势
- Web Components 全知道
- 前端应该知道的Wweb Components