这里所说的Web 组件指的是一套用于增强DOM 行为的工具,包括影子DOM、自定义
元素和HTML 模板。这一套浏览器API 特别混乱。
并没有统一的“Web Components”规范:每个Web 组件都在一个不同的规范中定义。
有些Web 组件如影子DOM和自定义元素,已经出现了向后不兼容的版本问题。
浏览器实现极其不一致。
由于存在这些问题,因此使用Web 组件通常需要引入一个Web 组件库,比如Polymer。这种库可以
作为腻子脚本,模拟浏览器中缺失的Web 组件。
HTML 模板
在Web 组件之前,一直缺少基于HTML 解析构建DOM 子树,然后在需要时再把这个子树渲染出
来的机制。一种间接方案是使用innerHTML 把标记字符串转换为DOM 元素,但这种方式存在严重的
安全隐患。另一种间接方案是使用document.createElement()构建每个元素,然后逐个把它们添加
到孤儿根节点(不是添加到DOM),但这样做特别麻烦,完全与标记无关。
相反,更好的方式是提前在页面中写出特殊标记,让浏览器自动将其解析为DOM子树,但跳过渲染。这正是HTML 模板的核心思想,而标签正是为这个目的而生的。下面是一个简单的
HTML 模板的例子:
I'm inside a template!
标签。这是因为
存在于一个包含在HTML 模板中的DocumentFragment 节点内。 在浏览器中通过开发者工具检查网页内容时,可以看到中的DocumentFragment: #document-fragment
I'm inside a template!
通过元素的content 属性可以取得这个DocumentFragment 的引用: console.log(document.querySelector('#foo').content); // #document-fragment 此时的DocumentFragment 就像一个对应子树的最小化document 对象。换句话说, DocumentFragment 上的DOM匹配方法可以查询其子树中的节点: const fragment = document.querySelector('#foo').content; console.log(document.querySelector('p')); // null console.log(fragment.querySelector('p')); //...
DocumentFragment 也是批量向HTML 中添加元素的高效工具。比如,我们想以最快的方式给某 个HTML 元素添加多个子元素。如果连续调用document.appendChild(),则不仅费事,还会导致多 次布局重排。而使用DocumentFragment 可以一次性添加所有子节点,最多只会有一次布局重排: // 开始状态: //
// // 期待的最终状态: //