python:从入门到精通
有几种类型的DOM节点。 有Document , Element , Text等等,它们也实现了广义Node 。 DocumentFragment节点是比较有趣的一种,但到现在为止还不那么常用。 基本上,它是用于节点的特殊容器。
在许多DOM算法中,对DocumentFragment节点进行了特殊处理。 在本文中,我们将看到一些旨在与DocumentFragment结合使用的API方法。 我们还将看到,节点容器的概念对于其他现代Web技术(例如<template>元素或整个影子DOM API)非常重要。 但是在开始之前,我们应该快速了解片段解析,它与DocumentFragment没有直接关系。
片段解析
HTML5解析器不仅仅可以用来解析完整的文档。 它也可以用于解析文档的一部分,称为片段。 设置诸如innerHTML或outerHTML属性将触发片段解析。 片段解析的工作方式与常规解析类似,但有一些例外。 最大的区别是需要上下文根。
被解析的片段可能被放置为某些元素的子元素,该元素可能具有也可能没有其他祖先。 该信息对于确定当前的解析模式至关重要,后者取决于当前树的层次结构。 此外,由于安全原因,片段分析将不会触发脚本执行。
因此,我们可能会使用以下代码,但不会看到其他输出。 脚本执行不会被触发。
var foo = document.querySelector('#foo');
foo.innerHTML = 'Hallo World!
使用片段解析是减少DOM操作的一种简单方法。 除了专门创建,更改和附加节点(这些节点都涉及上下文切换以及DOM操作)之外,我们仅致力于构造一个字符串,然后由解析器对其进行评估和处理。 因此,我们只有一个或几个DOM操作。 这种方法的缺点是我们需要解析器和更多JavaScript工作。 关键问题是:什么更耗时? 各种DOM操作是否比所有必需JavaScript字符串操作都要昂贵,还是相反?
显然,这取决于情况。 对于特定的情况,Grgur Grisogono使用几种方法进行了性能比较。 它还高度依赖于浏览器,尤其是JavaScript引擎的速度。 较高的值意味着更多的操作,因此是期望的。
尽管现在浏览器速度更快,但相对行为仍然有效。 这应该激励我们寻找更好的解决方案,并更多地了解DocumentFragment 。
汇总DOM操作
DocumentFragment节点背后的想法很简单: Node对象的容器。 附加DocumentFragment ,将其扩展为仅附加容器的内容,而不附加容器本身。 当请求DocumentFragment的深层副本时,其内容也会被克隆。 容器本身将永远不会附加到另一个节点,即使它必须具有所有者(即创建该片段的document如此。
创建DocumentFragment工作方式如下:
var fragment = document.createDocumentFragment();
从这一点开始, fragment行为与其他任何DOM父节点完全一样。 我们可以附加节点,删除节点或访问现有节点。 提供了使用querySelector和querySelectorAll运行CSS查询的选项。 最重要的是,如前所述,我们可以使用cloneNode()克隆节点。
用HTML制作模板
如果文档片段太大,为什么不使用它们进行模板制作呢? 好的, DocumentFragment无法用纯HTML构造,因为该概念仅通过DOM API公开。 因此,只能在JavaScript中创建容器。 这大大降低了使用效益。 目前,最流行的方法仍然是面向文本的。 我们首先将模板放在伪<script>元素中。 该元素是伪的,因为type属性将设置为无效的mime-type。 这样,将不会执行任何操作,但是元素的文本内容将使用不同的解析规则。
上图显示了标记化状态。 script标签的解析规则是特殊的,因为解析将以特殊的标记化状态进行。 HTML知道五个标记化状态,但是第五个标记化状态Plaintext对我们而言并不重要。 Rawtext状态与Script非常相似,它为我们提供了三种状态来说明。
让我们考虑一个例子。 我们使用三个元素分别代表其余三个州。 和其他许多元素一样, <div>元素也位于解析字符( PCData )体制中。 <textarea>使用RCData ,例如<title>元素。 更像原始字符的是Rawtext状态,可以使用<style>元素表示该状态。 在原始文本和脚本状态之间进行转义有细微的差异。 但是,在下面的讨论中,我们将它们视为等同。
var example = '
me & you > them';
var types = ["div", "textarea", "script"];
types.forEach(function (type) {
var foo = document.createElement(type);
foo.innerHTML = example;
console.log(foo.innerHTML);
})
也许我们会期望输出是相同的,但即使知道存在差异:谁知道它们的外观?
me & you > them
<br>me & you > them
me & you > them
只有最后一个与输入字符串完全匹配。 因此,我们有明显的赢家。 因此,这解释了为什么<script>元素如此流行以传输模板片段。 但是,这是有趣的部分开始的地方。 大多数模板引擎将根据字符串创建一个函数,该函数采用一个模型并为视图吐出生成的DOM节点列表。 有些可能已经绑定了值,具体取决于模型。 重要的部分是节点生成,至少在第一次迭代中,它主要是面向字符串的。
W3C意识到了这种情况,并通过引入<template>元素做出了React。 该元素可以理解为DocumentFragment载体。 由于DocumentFragment不直接参与DOM树,因此它通过属性附加到节点。 使用元素就像下面的示例一样简单:
{comment}
在DOM中,我们看不到该元素的任何子元素。 所有子项都已附加到所包含的DocumentFragment实例,可以通过content属性对其进行访问。
让我们得到这些孩子:
var fragment = document.querySelector('template').content;
var img = fragment.querySelector('img');
var comments = fragment.querySelectorAll('.comment');
文本已插入大括号中,以表明我们打算将其视为占位符。 没有自动填充它们的系统。
让我们创建一个函数为我们返回实例化的节点。 我们为上一个示例定制代码。
function createNodes (model) {
var fragment = document.querySelector('template').content;
var instance = fragment.clone(true);//deep cloning!
var img = instance.querySelector('img');
img.setAttribute('src', model.src);
img.setAttribute('alt', model.alt);
var div = instance.querySelector('div');
div.textContent = model.comment;
return instance;
}
通过遍历元素和子节点的所有属性,替换与属性和文本节点中的预定义结构匹配的文本,可以实现通用化。 最后,实例化的节点可以附加到以下位置:
var nodes = createNodes({
src: 'image.png',
alt: 'Image',
comment: 'Great!'
});
document.querySelector('#comments').appendChild(nodes);
<template>元素具有三个重要方面:
- 它将触发不同的解析模式。 因此,它不仅仅是一些元素。
- 它的子级不会附加到DOM,而是附加到可通过
content访问的DocumentFragment。 - 在使用之前,我们必须对片段进行深拷贝。
最后,文档片段非常有用,甚至可以用来使网站的一小部分重用和更加灵活。
影子DOM
近年来,对Web组件的需求激增。 许多前端框架都试图模仿一种Web组件结构。 但是,即使确实可以使用polyfill,也需要具有真正的DOM支持。 Polymer项目是出色的Polyfill的一个很好的例子,展示了可以使用Web组件完成的工作。
影子DOM允许我们做的就是将DocumentFragment附加到任何Element 。 存在三个约束:
-
DocumentFragment必须是特殊的—它必须是ShadowRoot。 - 每个
Element只能有一个ShadowRoot,或者没有。 -
ShadowRoot的内容与原始DOM分开。
这些约束会带来后果。
将ShadowRoot附加到元素的一个结果是不再渲染该元素—而是渲染了影子DOM中的内容。 但是,内容是有范围的,这意味着它可以遵循自己的样式规则。 整个事件处理过程也有所不同。
结果,引入了另一个新概念:插槽。 我们可以在影子DOM中定义插槽,该插槽中填充有来自元素的节点,该节点托管ShadowRoot 。 显然,创建带有阴影DOM的自定义元素是一个好主意。 整个自定义元素规范都是对此的回应。
那么我们如何使用影子DOM? 让我们做一些JavaScript来揭示API。 我们从以下HTML片段开始:
My header title
Some very important content
至此,一切正常。 这是需要我们JavaScript技能的地方:
var context = document.querySelector('#shadow-dialog');
var root = context.attachShadow({ mode: 'open' });
var headerSlot = document.createElement('slot');
headerSlot.name = 'header';
root.appendChild(headerSlot);
var contentSlot = document.createElement('slot');
contentSlot.name = 'content';
root.appendChild(contentSlot);
到目前为止,我们还没有收获太多。 我们从一些元素开始,然后又回来了。 有效地,组成的DOM树如下所示:
My header title
Some very important content
默认情况下,影子根的所有节点都分配给一个默认插槽(如果有)。 默认插槽没有name 。 那么我们获得了什么? 我们集成了一些透明的元素,恭喜! 但是更重要的是,我们的原始标记不必更改即可更改组成的DOM树的结构,属性或布局。 我们只需要更改将哪些元素附加到影子根即可,仅此而已。 我们实质上对前端进行了模块化。
现在有些人可能认为我们已经在服务器端使用了类似的技术。 当然,某些客户端框架也尝试聚合这样的代码。 但是,存在一些关键差异。 首先,我们拥有浏览器的全面支持(如果已实现)。 其次,沙箱简化了使用该模块的特定规则进行渲染的过程-与现有CSS规则没有冲突。 本质上可以保证该模块可以在每个页面上运行。 无需进行调试即可查看CSS规则在何处相互干扰。 最后,我们产生了更好的代码。 它易于生成且运输成本低廉,并且我们可以期待更高的性能。
结论
DocumentFragment是一个有用的帮助程序,它能够大幅减少DOM操作的数量。 它也是现代技术的重要基石,尤其是在Web组件领域。 它已经产生了两种非常出色的技术: <template>元素和ShadowRoot 。 前者大大简化了模板,为我们提供了不错的性能提速和一种传输预生成节点的优雅方式,而后者是Web组件的基础。
真的值得了解DocumentFragment吗? 可能还没有。 如果我们正在编写框架或库,那么绝对是必须的,但是大多数用户会很高兴在自己喜欢的前端库(例如jQuery,Angular等)中已经使用了它。 他们都在一个或多个位置使用DocumentFragment来克服潜在的性能问题。 虚拟DOM是否比真实DOM快? 是的,当然可以,但是如果不使用DocumentFragment聚合多个操作,它可能不会那么快。
翻译自: https://webdesign.tutsplus.com/tutorials/html5-mastery-fragments--cms-24842
python:从入门到精通
9534



被折叠的 条评论
为什么被折叠?



