文章首发于我的博客 https://github.com/mcuking/bl...

相干代码请查阅 https://github.com/mcuking/bl...

上一讲中 state 发生变化时,将会从新渲染,应用新生成的 dom 替换老的 dom。然而当 dom 树很大时,而每次更改的中央很小时,可能只是批改某个节点的属性,这种粗犷的替换形式就显得很节约性能。

因而 react 提出了 diff 算法:vnode(纯 js 对象) 代表 dom, 在渲染之前,先比拟出 oldvnode 和 newvode 的区别。而后增量的更新 dom。

如何增量更新呢?

复用 DOM

在第一讲中,render 函数里对于每一个断定为 dom 类型的 VDOM,都是间接创立一个新的 DOM:

...else if(typeof vnode.nodeName == "string") {    dom = document.createElement(vnode.nodeName)    ...}...

肯定要创立一个 新的 DOM 构造吗?

思考如下状况:
如一个组件, 首次渲染为 renderBefore, 调用 setState 再次渲染为 renderAfter 调用 setState 再再次渲染为 renderAfterAfter。 VNODE 如下:

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 机制如下:

  • 不同元素类型无奈复用
  • 雷同元素:

    • 更新属性
    • 复用子节点

因而代码大抵如下:

...else if(typeof vnode.nodeName == "string") {    if(!olddom || olddom.nodeName != vnode.nodeName.toUpperCase()) {        createNewDom(vnode, parent, comp, olddom)    } else {        diffDOM(vnode, parent, comp, olddom) // 包含 更新属性, 子节点复用    }}...

在前面的两讲中我将别离介绍更新属性以及复用子节点这两种机制。

相干文章

  • mini-react 实现原理解说 第一讲
  • mini-react 实现原理解说 第二讲
  • mini-react 实现原理解说 第三讲
  • mini-react 实现原理解说 第四讲
  • mini-react 实现原理解说 第五讲