文章首发于个人博客 https://github.com/mcuking/bl...相关代码请查阅 https://github.com/mcuking/bl...node
上一讲中 state 发生变化时,将会从新渲染,使用新生成的 dom 替换老的 dom。可是当 dom 树很大时,而每次更改的地方很小时,可能只是修改某个节点的属性,这种粗暴的替换方式就显得很浪费性能。react
所以 react 提出了 diff 算法:vnode(纯 js 对象) 表明 dom, 在渲染以前,先比较出 oldvnode 和 newvode 的区别。而后增量的更新 dom。git
如何增量更新呢?github
在第一讲中,render 函数里对于每个断定为 dom 类型的 VDOM,都是直接建立一个新的 DOM:算法
... else if(typeof vnode.nodeName == "string") { dom = document.createElement(vnode.nodeName) ... } ...
必定要建立一个 新的 DOM 结构吗?浏览器
考虑以下状况:
如一个组件, 初次渲染为 renderBefore, 调用 setState 再次渲染为 renderAfter 调用 setState 再再次渲染为 renderAfterAfter。 VNODE 以下:dom
const renderBefore = { tagName: 'div', props: { width: '20px', className: 'xx' }, children:[vnode1, vnode2, vnode3] } const renderAfter = { tagName: 'div', props: { width: '30px', title: 'yy' }, children:[vnode1, vnode2] } const renderAfterAfter = { tagName: 'span', props: { className: 'xx' }, children:[vnode1, vnode2, vnode3] }
renderBefore 和 renderAfter 都是 div,只不过 props 和 children 有部分区别,那咱们是否是能够经过修改 DOM 属性,修改 DOM 子节点,把 rederBefore 变化为 renderAfter 呢?函数
而 renderAfter 和 renderAfterAfter 属于不一样的 DOM 类型, 浏览器还没提供修改 DOM 类型的 Api,是没法复用的,所以必定要建立新的 DOM 的。性能
因此 diff 机制以下:spa
相同元素:
所以代码大体以下:
... else if(typeof vnode.nodeName == "string") { if(!olddom || olddom.nodeName != vnode.nodeName.toUpperCase()) { createNewDom(vnode, parent, comp, olddom) } else { diffDOM(vnode, parent, comp, olddom) // 包括 更新属性, 子节点复用 } } ...
在后面的两讲中我将分别介绍更新属性以及复用子节点这两种机制。
相关文章