zoukankan      html  css  js  c++  java
  • React Diff 算法

    React介绍

    React是Facebook开发的一款JS库,用于构建用户界面的类库。

    它采用声明式范例,可以传递声明代码,最大限度地减少与DOM的交互。

    特点:

    1. 声明式设计:React采用声明范式,你可以轻松描述你的应用
    2. 高效:React通过对DOM的模拟表现,最大限度地较少与DOM的交互。
    3. 灵活:React可以与你所知道的库或框架很好地工作。

    在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因

    React为此引入了虚拟DOM(Virtual DOM)的机制:

    在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个虚拟DOM树,然后React将当前整个虚拟DOM树和上一次的虚拟DOM树进行对比,得到虚拟DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。

    React Diff 算法

    虚拟DOM作为React的一大核心技术,了解其实现原理对于灵活应用有着很大帮助。

    Javascript 虚拟DOM对象

    在我们项目中申明一个组件是这样的:

    react.createElement('div', null, [
        // 创建一个img
        react.createElement('img', { src: "avatar.png", class: "profile" }),
        // 或者
        react.createElement('h1', null, [[user.firstName, user.lastName].join(' ')])
    ]);
    

    最后,React都会转换成类似这样的基本对象:

    {
        tagName: 'div',
        // 节点包含的属性
        properties: {
            style: {
                color: '#fff'
            }
        },
        // 子节点
        children: [],
        // 节点的唯一标识
        key: 1
    }
    

    Javascript DOM节点树

    然后,React把Javascript DOM模对象 转换成 Javascript DOM节点树:

    function create(vds, parent) {
      !Array.isArray(vds) && (vds = [vds]);
      // 如果没有父元素则创建个fragment来当父元素
      parent = parent || document.createDocumentFragment();
      var node;
      vds.forEach(function (vd) {
        // 如果是文字节点
        if (isText(vd)) {
          // 创建文字节点
          node = document.createTextNode(vd.text);
        } else {
          // 创建元素
          node = document.createElement(vd.tag);
        }
        // 将元素塞入父容器
        parent.appendChild(node);
        // 看看有没有子VNode,有孩子则处理孩子VNode
        vd.children && vd.children.length &&
          create(vd.children, node);
        // 看看有没有属性,有则处理属性
        vd.properties &&
          setProps({ style: {} }, vd.properties, node);
      });
      return parent;
    }
    

    Diff Algorithm

    现在我们得到的是Javascript 实现的虚拟DOM树,在一个事件循环中,当state或者preps变化时,React会创建一个新的虚拟DOM树,最后进行差异渲染。

    diff(previous:VTree, current:VTree) -> PatchObject

    React分三种情景:

    a. 分层对比

    layer


    React 仅仅是尝试把树按照层级分解. 这彻底简化了复杂度, 而且也不会失去很多, 因为 Web 应用很少有 component 移动到树的另一个层级去。它们大部分只是在相邻的子节点之间移动。

    b. 基于key匹配

    key

    Keys是一个VNode的唯一识别,用于对两个不同的VTree中的VNode做匹配的。通过key锁定某个组件后,React就可以直接对比这两个差异DOM节点树,复杂度为O(n)。

    所以这里有个性能优化的技巧。假设你有一个key组件,他的key属性为foo,后续又将它改为bar,那么React就会掉过DOM diff,同时完全弃置div所有自元素,从头渲染。在渲染大型子树以避免diff计算时,这样的设计很有用,因为我们知道这种计算就是在浪费时间。

    c. 基于自定义元素做优化

    component

    React提供自定义元素,所以匹配很简单。React 只会匹配相同 class 的 component。

    比如, 如果有个<Header><ExampleBlock>替换掉了,
    React 会删除掉 header 再创建一个example block。我们不需要化宝贵的时间去匹配两个不大可能有相似之处的 component。

    结束

    React在你调用 component 的 setState 方法的时候, 将其标记为 dirty,到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制。每次调用 setState 会重新计算整个子树.如果你想要提高性能, 尽量少调用 setState。

    最后, 你还有可能去掉一些子树的重新渲染,如果你在 component 上实现function shouldComponentUpdate(nextProps, nextState) 的话,你根据 component 的前一个和下一个 props/state,告诉 React 这个 component 没有更新, 也不需要重新绘制。

  • 相关阅读:
    题目1522:包含min函数的栈
    [Swift]LeetCode1157. 子数组中占绝大多数的元素 | Online Majority Element In Subarray
    [Swift]LeetCode1156. 单字符重复子串的最大长度 | Swap For Maximum Repeated Substring
    [Swift]LeetCode1153. 字符串转化 | String Transforms Into Another String
    [Swift]LeetCode1151. 最少交换次数来组合所有的 1 | Minimum Swaps to Group All 1's Together
    [Swift]LeetCode1154. 一年中的第几天 | Ordinal Number Of Date
    [Swift]LeetCode1152. 用户网站访问行为分析 | Analyze User Website Visit Pattern
    [Swift]LeetCode1150. 检查一个数是否在数组中占绝大多数 | Check If a Number Is Majority Element in a Sorted Array
    [Swift]LeetCode1146. 快照数组 | Snapshot Array
    [Swift]LeetCode1147. 段式回文 | Longest Chunked Palindrome Decomposition
  • 原文地址:https://www.cnblogs.com/freestyle21/p/4592277.html
Copyright © 2011-2022 走看看