作为React的核心概念之一,virtu【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。al dom从某种意义上改变了前端的现有【作者:唐霜】【原创内容,转载请注明出处】体系。大部分人把virtual dom当【原创内容,转载请注明出处】未经授权,禁止复制转载。做一种性能提升策略,因为它可以有效的减少【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】直接操作DOM带来的消耗,从而提升脚本的著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】执行效率。但是实际上,virtual d未经授权,禁止复制转载。【转载请注明来源】om并不完全是避免DOM操作的消耗,有的转载请注明出处:www.tangshuang.net【未经授权禁止转载】情况下,直接操作DOM可能来的更快,而v未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。irtual dom的真正意义,在于把直【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.net接的DOM操作和js编程,提炼成一个抽象【转载请注明来源】【访问 www.tangshuang.net 获取更多精彩内容】层,virtual dom是对js操作D【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。OM的抽象,对于今后的前端编程而言,de【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】veloper无需再直接操作DOM,而是本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】通过virtual dom去操作,这是它【本文受版权保护】原创内容,盗版必究。真正的意义。
【原创不易,请尊重版权】【本文首发于唐霜的博客】【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】本文是一篇对Virtual DOM原理和本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net理念进行阐述的文章,因此希望做到脱离Re【原创内容,转载请注明出处】未经授权,禁止复制转载。act,仅谈Virtual DOM.
【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】Virtual DOM的知识内容【访问 www.tangshuang.net 获取更多精彩内容】
转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】未经授权,禁止复制转载。Virtual DOM这个知识点的内容包【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.net括:
【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】【原创不易,请尊重版权】- 将DOM树抽象成一个js对象【本文受版权保护】 本文版权归作者所有,未经授权不得转载。【关注微信公众号:wwwtangshuangnet】转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net
- 将抽象的Virtual DOM转换为真正本文作者:唐霜,转载请注明出处。【原创不易,请尊重版权】的DOM 本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】未经授权,禁止复制转载。【转载请注明来源】
- Virtual DOM发生变化时,本文版权归作者所有,未经授权不得转载。自动原创内容,盗版必究。更新真正的DOM。包含两个内容:
【本文受版权保护】
- 通过一个diff算法,来比较新旧Virt转载请注明出处:www.tangshuang.net【转载请注明来源】ual DOM之间的差异,并记录下来 原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】
- 将记录下来的差异,通过一个patch算法【访问 www.tangshuang.net 获取更多精彩内容】【原创不易,请尊重版权】操作真实的DOM 【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】
原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。
接下来,我们就会对这些内容一一进行详解。
【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】未经授权,禁止复制转载。【原创不易,请尊重版权】【原创不易,请尊重版权】Virtual DOM的创建【本文受版权保护】
【版权所有,侵权必究】未经授权,禁止复制转载。【作者:唐霜】将DOM树抽象成一个js对象,这个对象就【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。是Virtual DOM。应该生成怎样的【作者:唐霜】著作权归作者所有,禁止商业用途转载。对象,这个对象有怎样的数据结构,生成的时【转载请注明来源】【本文受版权保护】候使用怎样的高效算法?
转载请注明出处:www.tangshuang.net原创内容,盗版必究。【本文首发于唐霜的博客】未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。Virtual DOM的数据结构【本文首发于唐霜的博客】
转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】和DOM一样,Virtual DOM也是转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】由一个一个的节点组成,节点之间有父子关系原创内容,盗版必究。【原创内容,转载请注明出处】,一个节点和它的子节点的引用,我们把组成本文作者:唐霜,转载请注明出处。【作者:唐霜】的一个单元称为VNode(Virtual原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。 DOM Node),一个VNode应该【作者:唐霜】未经授权,禁止复制转载。包含哪些信息呢?
【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。{
id: 1, // 标记这个VNode的唯一id,整个VTree可以完全用这些id图示出来,在diff中也有作用
name: 'div', // 标签名,甚至可以是自定义标签
props: { // 标签的属性
'id': 'my-tag',
'data-role': 'test',
'class': 'my-class the-class',
},
children: [ // 子VNode列表
{ ...another VNode... },
...
],
text: '...', // textNode, 如果标签直接包含文本内容的话,使用text,而非children
events: { // 绑定的事件
input: [ function(e) { ... } ],
},
}
上面的VNode仅是一个参考,而非标准,转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】各框架(库)实现与此不同。但大体上,一个【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.netVNode对象,应该包含一个tag的完整【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】内容,甚至它的上下文引用。
【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】使用JSX生成Virtual DOM本文作者:唐霜,转载请注明出处。
本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net【本文受版权保护】在代码层面,我们是看不到Virtual 【转载请注明来源】本文作者:唐霜,转载请注明出处。DOM的真实内容的,我们无法通过cons【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。ole.log得到这个原始的js对象,因【本文首发于唐霜的博客】未经授权,禁止复制转载。此也无法直接修改它。它是被封装在框架源码未经授权,禁止复制转载。【转载请注明来源】内部的一套格式,我们只管使用库提供的一些本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。方法来生成它,而不用关心它是怎样的一种存未经授权,禁止复制转载。【本文受版权保护】在。当然,如果你要自己实现一套自己的Vi【作者:唐霜】未经授权,禁止复制转载。rtual DOM,也可以自己定义这些内【原创不易,请尊重版权】著作权归作者所有,禁止商业用途转载。容。
【未经授权禁止转载】【本文首发于唐霜的博客】【转载请注明来源】原创内容,盗版必究。既然我们只能利用一些方法来生成Virtu【原创内容,转载请注明出处】【版权所有,侵权必究】al DOM,那么就要找最简单的,那就是著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.netJSX。JSX用类似HTML的语法,实现【本文受版权保护】转载请注明出处:www.tangshuang.netVirtual DOM的生成。在掌握了少【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。数与HTML不同的地方之后,使用JSX就【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】像写HTML一样方便且直观易于理解。
著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】【作者:唐霜】著作权归作者所有,禁止商业用途转载。例如,我们可以这样:转载请注明出处:www.tangshuang.net
【版权所有,侵权必究】【原创不易,请尊重版权】【转载请注明来源】var node = (
<div id="my-tag" className="my-class the-class" dataRole="test" onInput={inputFunction}>
<span>text</span>
</div>
)
render(node)
用一段类似HTML的代码就可以生成一段V【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】Node(包括children VNod著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。es),而无需我们手工去写复杂的js对象【本文首发于唐霜的博客】原创内容,盗版必究。。
【版权所有,侵权必究】【本文受版权保护】【转载请注明来源】实际上,在不同的框架体系里面,jsx的编未经授权,禁止复制转载。【未经授权禁止转载】译结果也不同,在React里面,会被解析【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】为React.creatElement,而在vue里面,也会有自己的解析结果。未经授权,禁止复制转载。未经授权,禁止复制转载。但是JSX给了我们统一的生成Virtua本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。l DOM的接口,让我们可以不必因为不同转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。的框架而撰写不同的代码。在不同的框架中,原创内容,盗版必究。【本文首发于唐霜的博客】使用该框架提供的render函数去解析j转载请注明出处:www.tangshuang.net【版权所有,侵权必究】sx即可。
render函数会将Virtual DO转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】M渲染成真正的DOM,这我们将在下一节详本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】细讲到。
【作者:唐霜】本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net将Virtual DOM渲染成真实的DO原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。M
【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】上一节提到,render函数会将Virt【原创不易,请尊重版权】原创内容,盗版必究。ual DOM渲染为真实的DOM,虽然我著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】们不用关心render的实现是怎么样的,【本文受版权保护】原创内容,盗版必究。但是为了了解其原理,还是要从代码层面去假【本文受版权保护】转载请注明出处:www.tangshuang.net装实现它。
【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。【本文受版权保护】本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。现在,我们有了Virtual DOM(一转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。个js对象),我们要利用它构建真实的DO转载请注明出处:www.tangshuang.net【本文受版权保护】M。在js中,我们使用document.createElement来创建一个标签,所以,实际上,渲染DOM本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】只需要两步:createElement和【作者:唐霜】【原创内容,转载请注明出处】insertElement。
function createElement(VNode) {
let node = document.createElement(VNode.name)
let props = VNode.props
let keys = Object.keys(props)
keys.forEach(prop => node[prop] = props[prop]) // 这里只是做个演示,实际上还需要详细考虑
return node}function insertElement(createdNode, parentNode, replaceNode) {
replaceNode ? parentNode.replaceChild(createdNode, replaceNode) : parentNode.appendChild(createdNode)
}
大致便是上面的思路,当然,在库具体实现的【本文首发于唐霜的博客】【原创内容,转载请注明出处】时候会复杂的多,比如事件绑定,比如在se【本文受版权保护】【转载请注明来源】tInnerHTML的时候,要替换某个元著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net素怎么办?但是,思路上就是这样,通过Vi【转载请注明来源】本文版权归作者所有,未经授权不得转载。rtual DOM创建一个真实的DOM元著作权归作者所有,禁止商业用途转载。【转载请注明来源】素,再将它挂载到适当的文档流中,或替换掉【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net原来的某个元素。
【转载请注明来源】【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】diff算法转载请注明出处:www.tangshuang.net
转载请注明出处:www.tangshuang.net【转载请注明来源】【版权所有】唐霜 www.tangshuang.net【作者:唐霜】本文作者:唐霜,转载请注明出处。上述insetElement方法直接替换【转载请注明来源】未经授权,禁止复制转载。DOM或将Node插入到DOM中,虽然是【本文首发于唐霜的博客】【访问 www.tangshuang.net 获取更多精彩内容】由Virtual DOM转化而来,但是还本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。不足以体现Virtual DOM的特性。【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。Virtual DOM包含的一项内容,就【版权所有,侵权必究】本文作者:唐霜,转载请注明出处。是在上述insertElement之前,著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。要进行新老Virtual DOM的对比,【转载请注明来源】著作权归作者所有,禁止商业用途转载。找出两者之间真正的不同之处,仅对这些不同【原创不易,请尊重版权】原创内容,盗版必究。之处进行更新。
【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net【原创内容,转载请注明出处】现在的问题是,如何知道哪些VNode节点【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】是要被更新的呢?这就需要一个diff算法【本文首发于唐霜的博客】【转载请注明来源】来进行对比,而对比的时候,我们仅用到Vi本文版权归作者所有,未经授权不得转载。【转载请注明来源】rtual DOM作为对比的对象,而不涉【本文受版权保护】【版权所有】唐霜 www.tangshuang.net及真实DOM。Virtual DOM更新本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。视图之所以快,也是因此。
未经授权,禁止复制转载。【本文首发于唐霜的博客】未经授权,禁止复制转载。传统diff算法【未经授权禁止转载】
【本文受版权保护】未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。传统 diff 算法【转载请注明来源】通过循环递归对节点进行依次对比,效率低下本文作者:唐霜,转载请注明出处。【版权所有】唐霜 www.tangshuang.net,算法复杂度达到 O(n3【关注微信公众号:wwwtangshuangnet】),其中 n 是树中节点的总数。我们用代【作者:唐霜】著作权归作者所有,禁止商业用途转载。码来演示传统diff算法:
【本文首发于唐霜的博客】【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。let result = [];// 比较叶子节点const diffLeafs = function(beforeLeaf, afterLeaf) {
// 获取较大节点树的长度
let count = Math.max(beforeLeaf.children.length, afterLeaf.children.length);
// 循环遍历
for (let i = 0; i < count; i++) {
const beforeTag = beforeLeaf.children[i];
const afterTag = afterLeaf.children[i];
// 添加 afterTag 节点
if (beforeTag === undefined) {
result.push({type: "add", element: afterTag});
// 删除 beforeTag 节点
}
else if (afterTag === undefined) {
result.push({type: "remove", element: beforeTag});
// 节点名改变时,删除 beforeTag 节点,添加 afterTag 节点
} else if (beforeTag.tagName !== afterTag.tagName) {
result.push({type: "remove", element: beforeTag});
result.push({type: "add", element: afterTag});
// 节点不变而内容改变时,改变节点
} else if (beforeTag.innerHTML !== afterTag.innerHTML) {
if (beforeTag.children.length === 0) {
result.push({
type: "changed",
beforeElement: beforeTag,
afterElement: afterTag,
html: afterTag.innerHTML
});
} else {
// 递归比较
diffLeafs(beforeTag, afterTag);
}
}
}
return result;
}
如果一棵树有1000个节点,那么如果全部【作者:唐霜】著作权归作者所有,禁止商业用途转载。进行比较,将会进行10亿次比较。以现代的原创内容,盗版必究。【版权所有,侵权必究】计算机CPU算力,消耗的时间还是比较多的【版权所有】唐霜 www.tangshuang.net本文作者:唐霜,转载请注明出处。。因此,这不能作为virtual dom【本文首发于唐霜的博客】【版权所有,侵权必究】的diff算法来提升性能。
转载请注明出处:www.tangshuang.net【版权所有,侵权必究】【关注微信公众号:wwwtangshuangnet】【原创内容,转载请注明出处】【转载请注明来源】React的diff算法【转载请注明来源】
【本文受版权保护】未经授权,禁止复制转载。【本文受版权保护】原创内容,盗版必究。【本文受版权保护】React团队在研究了传统diff算法之未经授权,禁止复制转载。本文版权归作者所有,未经授权不得转载。后,结合web的特征,认为可以通过以下策著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。略来简化传统diff算法:
【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】【版权所有,侵权必究】- Web UI 中 DOM 节点跨层级的移【作者:唐霜】著作权归作者所有,禁止商业用途转载。动操作特别少,可以忽略不计。 本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net
- 拥有相同类的两个组件将会生成相似的树形结本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net构,拥有不同类的两个组件将会生成不同的树转载请注明出处:www.tangshuang.net【作者:唐霜】形结构。 转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】著作权归作者所有,禁止商业用途转载。
- 对于同一层级的一组子节点,它们可以通过唯【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。一 id 进行区分。 【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net
基于上面的策略,React团队直接将传统【本文受版权保护】未经授权,禁止复制转载。diff算法中一些比较干掉,比如不去比较【本文首发于唐霜的博客】原创内容,盗版必究。父节点不同的子节点,这样下来,复杂度直接【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】降到了O(n)。
本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.nettree diff【版权所有】唐霜 www.tangshuang.net
【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】不考虑DOM节点的跨级移动,在diff的【转载请注明来源】【未经授权禁止转载】时候,只对同一级的节点进行比较。
【关注微信公众号:wwwtangshuangnet】【转载请注明来源】【未经授权禁止转载】原创内容,盗版必究。
仅对同级节点进行比较,当同级节点存在差异本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】时,不再进行子节点的比较
未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net【作者:唐霜】原创内容,盗版必究。【本文首发于唐霜的博客】反过来说,两个节点是否要进行比较,只需要【版权所有,侵权必究】【本文首发于唐霜的博客】看他们是否属于相同的父节点。
【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】【未经授权禁止转载】【访问 www.tangshuang.net 获取更多精彩内容】在比较过程中,当新的Virtual DO【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.netM中某个VNode不存在于老的Virtu【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】al DOM中(同级同位置),那么这个V原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。Node就会被认为是新增的节点。而如果某【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。个VNode在老的Virtual DOM著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】中有,新的Virtual DOM中没有,本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。那么认为这个节点会被删除。
【原创内容,转载请注明出处】【本文受版权保护】转载请注明出处:www.tangshuang.net【版权所有,侵权必究】【作者:唐霜】因为没有考虑移动的情况,所以,当在预期发本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net生移动时,diff算法会怎么认为呢?比如原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。:
【本文首发于唐霜的博客】【关注微信公众号:wwwtangshuangnet】

【本文受版权保护】本文版权归作者所有,未经授权不得转载。
diff算法不会把这种情况当做移动来处未经授权,禁止复制转载。【本文受版权保护】理,diff算法会认为,新的Virtua【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.netl DOM中,原来的A节点会被删除,在D【原创内容,转载请注明出处】未经授权,禁止复制转载。节点下会新增一个节点。这个新增的节点跟原【版权所有,侵权必究】【版权所有】唐霜 www.tangshuang.net来的A节点没有任何关系,虽然结构一样,但原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。实际上它是全新的。
虽然这一做法不是很严谨,但是在实际的we【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】b项目中这样做并没有带来非常大的性能问题【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。,相反,在处理细碎的大量变动时,反而带来本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net了性能优势。不过React官方也建议,应【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.net该保持你的组件dom结构的稳定性,不应出本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】现这种移动节点的现象。同时,由于A节点被【关注微信公众号:wwwtangshuangnet】【作者:唐霜】移除时会被完全销毁,它的子节点也就跟着被【本文受版权保护】未经授权,禁止复制转载。销毁了,如果你要再次显示出来,就必须创建【版权所有,侵权必究】原创内容,盗版必究。新的元素,所以,比较好的办法是使用css【访问 www.tangshuang.net 获取更多精彩内容】转载请注明出处:www.tangshuang.net类来控制它的显示和隐藏,而不是通过数据变【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.net化因此Virtual DOM变化来更新视【本文受版权保护】【版权所有,侵权必究】图。
【版权所有,侵权必究】著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】【转载请注明来源】【本文首发于唐霜的博客】component diff【未经授权禁止转载】
【未经授权禁止转载】转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。React以组件作为自己的产出目标,组件【本文首发于唐霜的博客】【本文首发于唐霜的博客】与组件之间可以存在嵌套关系,所以组件本身【访问 www.tangshuang.net 获取更多精彩内容】本文作者:唐霜,转载请注明出处。的DOM也会遵循Virtual DOM机原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。制。
本文作者:唐霜,转载请注明出处。【转载请注明来源】【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。- 如果是同一类型的组件,按照tree di转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。ff策略继续比较 Virtual DOM转载请注明出处:www.tangshuang.net本文作者:唐霜,转载请注明出处。。 【作者:唐霜】【转载请注明来源】【访问 www.tangshuang.net 获取更多精彩内容】【未经授权禁止转载】
- 如果不是,则将该组件判断为 dirty 转载请注明出处:www.tangshuang.net本文版权归作者所有,未经授权不得转载。component,从而替换整个组件下的【版权所有,侵权必究】【未经授权禁止转载】所有子节点。 【原创内容,转载请注明出处】原创内容,盗版必究。本文作者:唐霜,转载请注明出处。【作者:唐霜】【版权所有】唐霜 www.tangshuang.net
- 对于同一类型的组件,有可能其 Virtu本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。al DOM 没有任何变化,如果能够确切【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】的知道这点那可以节省大量的 diff 运【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】算时间,因此 React 允许用户通过
shouldComponentUpdate()来判断该组件是否需要进行 diff。【作者:唐霜】 原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】转载请注明出处:www.tangshuang.net
虽然两个组件的DOM可能完全相似,但是这原创内容,盗版必究。【原创内容,转载请注明出处】种情况就没有必要创建两个组件,React【作者:唐霜】【未经授权禁止转载】官方认为不同类型的 component 未经授权,禁止复制转载。【本文首发于唐霜的博客】是很少存在相似 DOM tree 的机会【原创内容,转载请注明出处】【原创内容,转载请注明出处】,因此这种极端因素很难在实现开发过程中造转载请注明出处:www.tangshuang.net【转载请注明来源】成重大影响的,因此他们毅然决然的采用了上【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net述component diff策略,即使未经授权,禁止复制转载。未经授权,禁止复制转载。可能存在一定的性能误差。
【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.netelement diff【作者:唐霜】
【版权所有,侵权必究】原创内容,盗版必究。【未经授权禁止转载】【原创内容,转载请注明出处】当节点处于同一层级时,React dif【未经授权禁止转载】【未经授权禁止转载】f 提供了三种节点操作,分别为:INSE未经授权,禁止复制转载。【本文受版权保护】RT_MARKUP(插入)、MOVE_E【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。XISTING(移动)和 REMOVE_本文作者:唐霜,转载请注明出处。【转载请注明来源】NODE(删除)。
【作者:唐霜】【未经授权禁止转载】【转载请注明来源】本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】和tree diff不同,当对比同一组元【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。素时,比如老的Virtual DOM中有本文作者:唐霜,转载请注明出处。【未经授权禁止转载】一组p标签,和新的Virtual DOM【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net中的这组p标签是同一个父节点,那么就要考原创内容,盗版必究。原创内容,盗版必究。虑这组p标签的实际情况,而不是一味的删掉【本文受版权保护】本文版权归作者所有,未经授权不得转载。再插入进来。比如新的Virtual DO转载请注明出处:www.tangshuang.net【作者:唐霜】M中,多了一个p标签,那么它可能是插入进【关注微信公众号:wwwtangshuangnet】【版权所有】唐霜 www.tangshuang.net来的;或者其中一个移动了;或者其中某个删本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】除了。这个时候,在上面的Virtual 未经授权,禁止复制转载。【版权所有,侵权必究】DOM数据结构中的id就发挥作用了,在R【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。eact中使用key作为这个标志。当一组p原本的id序列是1著作权归作者所有,禁止商业用途转载。【版权所有,侵权必究】,2,3,4,5时,现在变成了4,2,1【关注微信公众号:wwwtangshuangnet】【转载请注明来源】,5,3,那么可以通过重新排列它们就可以【本文首发于唐霜的博客】【本文受版权保护】了。而如果现在变成了1,3,4,5,6,未经授权,禁止复制转载。原创内容,盗版必究。那么可以知道,2被删除了,6被插入进来。

所以,给每一个VNode一个唯一标识只非【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。常重要,如果不存在这个标识,那么当遇到上【关注微信公众号:wwwtangshuangnet】【关注微信公众号:wwwtangshuangnet】述情况的时候,diff算法无法知道当前新【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】的Virtual DOM中的元素和老的V【作者:唐霜】本文版权归作者所有,未经授权不得转载。irtual DOM中的元素是不是一样的【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。,他们虽然name值可能一样,但是可能存未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】在上下文的差别,比如绑定的事件不同,所以【关注微信公众号:wwwtangshuangnet】【访问 www.tangshuang.net 获取更多精彩内容】diff算法在无法获取这个唯一标识的情况【本文首发于唐霜的博客】【版权所有,侵权必究】下,只要发现不同,就会把对应那个位置的元【转载请注明来源】未经授权,禁止复制转载。素及其之后的所有元素都销毁,把新的Vir未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.nettual DOM中对应的元素加进来,这样本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。性能上就差了一大截。
【关注微信公众号:wwwtangshuangnet】【未经授权禁止转载】原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net差异的记录原创内容,盗版必究。
【转载请注明来源】【本文首发于唐霜的博客】【作者:唐霜】【原创不易,请尊重版权】【转载请注明来源】diff算法的目标,是找出Virtual本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。 DOM在前后的差异,并把这些差异保存起著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。来。但是有一个问题,保存在哪里?老的Vi【本文受版权保护】【关注微信公众号:wwwtangshuangnet】rtual DOM保存在哪里?差异的记录【本文首发于唐霜的博客】转载请注明出处:www.tangshuang.net保存在哪里?
【本文首发于唐霜的博客】【作者:唐霜】【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。React是以组件为单位的,因此,这个组本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。件的Virtual DOM被保存在组件的原创内容,盗版必究。【本文首发于唐霜的博客】特定属性里。当组件的数据发生变化的时候,【原创内容,转载请注明出处】【作者:唐霜】会产生一份新的Virtual DOM,这未经授权,禁止复制转载。【版权所有,侵权必究】份新的Virtual DOM会被用来和保转载请注明出处:www.tangshuang.net【转载请注明来源】存起来的老的Virtual DOM进行对未经授权,禁止复制转载。原创内容,盗版必究。比,当后续操作(patch)完成之后,新【关注微信公众号:wwwtangshuangnet】【版权所有,侵权必究】的Virtual DOM会被保存到该属性原创内容,盗版必究。【本文首发于唐霜的博客】上把老的给覆盖。
【原创内容,转载请注明出处】【版权所有,侵权必究】【本文首发于唐霜的博客】【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。diff算法没发现一个差异,就会把该差异【本文受版权保护】【版权所有】唐霜 www.tangshuang.net记录到一个特定的变量中,保存差异的变量是原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。一个数组,该数组的每个元素是一个对象,记著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net录着差异的类型、结果、需要进行的操作。
【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】【本文首发于唐霜的博客】【关注微信公众号:wwwtangshuangnet】function enqueueInsertMarkup(parentInst, markup, toIndex) {
updateQueue.push({
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
markupIndex: markupQueue.push(markup) - 1,
content: null,
fromIndex: null,
toIndex: toIndex,
});
}
function enqueueMove(parentInst, fromIndex, toIndex) {
updateQueue.push({
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.MOVE_EXISTING,
markupIndex: null,
content: null,
fromIndex: fromIndex,
toIndex: toIndex,
});
}
function enqueueRemove(parentInst, fromIndex) {
updateQueue.push({
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.REMOVE_NODE,
markupIndex: null,
content: null,
fromIndex: fromIndex,
toIndex: null,
});
}
上面的updateQueue就是这个变量未经授权,禁止复制转载。原创内容,盗版必究。,它里面记录着每一个差异,其中有一个ty【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。pe属性,用来表示这个差异后续应该执行怎【本文受版权保护】【版权所有】唐霜 www.tangshuang.net样的操作。
本文作者:唐霜,转载请注明出处。【本文受版权保护】本文作者:唐霜,转载请注明出处。patch算法原创内容,盗版必究。
【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】【本文受版权保护】【关注微信公众号:wwwtangshuangnet】当知道了哪些地方是有差异的,需要更新真实原创内容,盗版必究。【访问 www.tangshuang.net 获取更多精彩内容】的DOM之后,我们就可以对真实DOM进行【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】操作了。虽然我们可以像第二部分中一样,用【版权所有,侵权必究】著作权归作者所有,禁止商业用途转载。一个render函数去重新渲染,但是通过【未经授权禁止转载】【原创不易,请尊重版权】这里的patch算法进行操作,效率会高很【本文首发于唐霜的博客】【本文受版权保护】多。
本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。【作者:唐霜】【关注微信公众号:wwwtangshuangnet】function patch (node, patches) {
var walker = {index: 0}
dfsWalk(node, walker, patches)
}
function dfsWalk (node, walker, patches) {
var currentPatches = patches[walker.index] // 从patches拿出当前节点的差异
var len = node.childNodes ? node.childNodes.length : 0
for (var i = 0; i < len; i++) { // 深度遍历子节点
var child = node.childNodes[i]
walker.index++
dfsWalk(child, walker, patches)
}
if (currentPatches) {
applyPatches(node, currentPatches) // 对当前节点进行DOM操作
}
}
function applyPatches(node, currentPatches) {
currentPatches.forEach(function (currentPatch) {
switch (currentPatch.type) {
case REPLACE:
node.parentNode.replaceChild(currentPatch.node.render(), node)
break
case REORDER:
reorderChildren(node, currentPatch.moves)
break
case PROPS:
setProps(node, currentPatch.props)
break
case TEXT:
node.textContent = currentPatch.content
break
default:
throw new Error('Unknown patch type ' + currentPatch.type)
}
})
}
这里红色的currentPatches就本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net是我们前面的updateQueue。其中【本文受版权保护】【版权所有】唐霜 www.tangshuang.net,reorder和setProps都有具著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。体的实现方法,无非都是通过JavaScr【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】ipt原生的dom操作方法进行操作。
未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。【转载请注明来源】转载请注明出处:www.tangshuang.net【本文首发于唐霜的博客】数据响应【转载请注明来源】
本文作者:唐霜,转载请注明出处。【版权所有,侵权必究】未经授权,禁止复制转载。Virtual DOM确实让DOM操作变未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。得抽象很多,但是还需要和数据结合起来。数【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。据的变化会带来视图的变化。
转载请注明出处:www.tangshuang.net【作者:唐霜】未经授权,禁止复制转载。
当model上的数据发生变化时,自动触发本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。virtual dom的diff算法和p【版权所有】唐霜 www.tangshuang.net【本文受版权保护】atch操作
【未经授权禁止转载】【未经授权禁止转载】著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。对数据的监听是model层面的逻辑,当监转载请注明出处:www.tangshuang.net【版权所有,侵权必究】听到数据有所变化之后,需要重新调用ren【转载请注明来源】【未经授权禁止转载】der方法,调用时就会触发diff算法,【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】进而进行patch操作:
【原创不易,请尊重版权】【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。import {Component} from 'react'
class App extends Component {
constructor(props) {
super(props)
this.state = {
value: '',
}
}
handleChange(e) {
let {value} = e.target
this.setState({ value })
}
render() {
return (
<div>
<input value={value} onChage={this.handleChange} />
<span>{value}</span>
</div>
)
}
}
在React中,执行上面红色的setSt【原创内容,转载请注明出处】著作权归作者所有,禁止商业用途转载。ate的时候,React内部会自己去调用【版权所有】唐霜 www.tangshuang.net【作者:唐霜】render函数,得到Virtual D【作者:唐霜】【版权所有】唐霜 www.tangshuang.netOM,完成Virtual DOM的dif【原创不易,请尊重版权】【本文受版权保护】f和patch。当你自己实现了setSt本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.netate方法之后,你也就基本实现React本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net核心的功能。
本文作者:唐霜,转载请注明出处。【本文受版权保护】转载请注明出处:www.tangshuang.net小结原创内容,盗版必究。
【本文受版权保护】【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。【访问 www.tangshuang.net 获取更多精彩内容】在文章开头,我提到Virtual DOM【本文受版权保护】原创内容,盗版必究。的意义在于抽象。这是因为在性能上,其实它著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。并没有占太大的便宜,只能说还过的去,从上【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】面的分析中你就可以知道,非得用Virtu【未经授权禁止转载】【转载请注明来源】al DOM来更新视图的话,有些原本用一【原创内容,转载请注明出处】原创内容,盗版必究。个innerText就搞定的操作,却要在【作者:唐霜】原创内容,盗版必究。代码内部溜一大圈儿。但是,正是由于这种统著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】一的封装,让你不用再去编写一整套的根据数【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。据渲染DOM,监听数据更新DOM的操作。【原创内容,转载请注明出处】转载请注明出处:www.tangshuang.net这让Virtual DOM在原始的js编【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】程基础上更进了一步。将Virtual D本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】OM应用到React Native,js未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。程序就不用再关心底层是浏览器提供的DOM著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】,还是手机提供的UI接口,在通过js去更原创内容,盗版必究。【作者:唐霜】新视图这个代码层面,就得到了统一。这才是【作者:唐霜】本文版权归作者所有,未经授权不得转载。Virtual DOM最大的意义。
【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】【原创不易,请尊重版权】2017-08-24 9821 React, Virtual DOM



您写的传统diff算法的代码演示,就是分层比较吧。
我看网上的搜索,传统的思路应该是对新的树每个节点遍历查找旧的树n^2,然后找到最小编辑距离n,合起来是n^3。但是这一块我在网上没找到代码。
是有问题
[…] 在我写完《Virtual DOM原理浅易详解》之后,我打算把Virtual DOM的体系拆解开。其中非常重要的一点,是我打算做一个HTML的解析器,在通过fetch抓取到某个网页之后,可以通过这个解析器,快速得到自己想要的数据。而这一部分,是Virtual DOM整个知识体系的一部分,即“DOM树抽象成一个js对象”这个部分。于是,我希望通过本文,详细阐述我是怎么创建自己的这个抽象js对象。 […]