Web Components 标准非常重要的一个特性是,它使开发者能够将HTML页面的功能封装为 custom elements(自定义标签)。而自定义标签的好处,就是在大型web开发的时候,可以封装组件(Vue、Angular等大型框架)来重用,方便开发跟管理。
在自定义标签之前,先来看下几个相关的API:
- Object.create(proto, [propertiesObject]): 创建新对象,将该新对象的__proto__指向proto,这里的proto是 HTMLElement,因为我们需要创建一个element。主要是ES5的写法,ES6可以通过 Class 跟 constructor 来实现。
- document.currentScript.ownerDocument: 该方法主要针对通过 HTML import导入的文件,用于获取自定义element的上下文document,并通过该document 获取该 element 定义的html模板。因为document是指向全局上下文的,获取不到自定义的元素。document.currentScript: 获取正在执行的<script>脚本。
- createdCallback: 创建元素成功之后的回调函数。将自定义元素的模板添加到该元素下面。
- Element.createShadowRoot: 创建 shadow DOM,将该标签模板与外部元素隔离开来,不受外部样式影响,也不影响外部样式。不过MDN上将这个API废弃掉了,建议使用 attachShadow() 替代
- Element.attachShadow(shadowRootInit): createShadowRoot 的替代者。shadowRootInit: 指定shadowRoot的封装模式 {mode: open/closed}。open 表示可在外部 获取到该shadow,closed 反之。
- open: ele.shadowRoot === shadow
- closed: ele.shadowRoot === null
- document.registerElement(tag-name, options): 注册自定义元素。MDN已经废弃这个API了,建议使用 customElements.define() 替代
- tag-name: 自定义标签的名字,必须有一个连字符 '-'
- options: 指定标签的原型属性跟可扩展对象
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 <link rel="import" href="./customized-tag.html"> 6 <style type="text/css"> 7 .red-color { 8 color: blue 9 } 10 </style> 11 </head> 12 <body> 13 <div class="red-color">aaa</div> 14 <customized-tag></customized-tag> 15 </body> 16 </html>
1 <template> 2 <div class="red-color"> 3 hahaha 4 </div> 5 <style type="text/css"> 6 /*.red-color { 7 color: red; 8 }*/ 9 </style> 10 </template> 11 <script type="text/javascript"> 12 (function() { 13 // 创建一个html元素 14 let ele = Object.create(HTMLElement.prototype); 15 // 获取当前文档的元素节点里面的内容 16 // 因为这个文件是通过 import 导入到父html的, document无法直接获取到自定义的模板 17 // document.currentScript 获取到当前的script,因为默认 document 是之前全局上下文的 18 // document.currentScript.ownerDocument 获取当前文档,而不是全局 19 let customizedDoc = document.currentScript.ownerDocument.querySelector('template').content; 20 ele.createdCallback = function() { 21 // 创建shadow DOM,避免元素相互影响,已废弃,建议使用 attachShadow 22 // let shadow = this.createShadowRoot(); 23 24 // 创建shadow DOM, mode: open / closed, open 表示可以在js里面获取到该shadowDOM,closed 表示无法在外部获取到该shadow DOM 25 // open: ele.shadowRoot === shadow 26 // closed: ele.shadowRoot === null 27 let shadow = this.attachShadow({mode: 'open'}); 28 // 拷贝节点,第二个参数表示是否导入后代节点 29 let clone = document.importNode(customizedDoc, true); 30 shadow.appendChild(clone); 31 32 // 如果不想创建 shadowDOM, 可直接将节点append到父元素 33 // this.appendChild(clone) 34 } 35 document.registerElement('customized-tag', { 36 prototype: ele 37 }) 38 })() 39 40 </script>
使用 customElements.define(tag-name, constructor, options) 方式注册组件
1 class CustomizedTag extends HTMLElement { 2 constructor() { 3 super(); 4 this.attachToTag(); 5 } 6 7 attachToTag() { 8 let shadow = this.attachShadow({mode: 'open'}) 9 let ele = document.currentScript.ownerDocument.querySelector('template').content; 10 let clone = document.importNode(ele, true); 11 shadow.appendChild(clone) 12 } 13 } 14 customElements.define('customized-tag', CustomizedTag)