你不知道的react更新文本的细节-textContent vs nodeValue vs data

157 阅读2分钟

起因

笔者在研究ReactDOM-Diff源码的时候,看到了react中的这个方法setTextContent,于是好奇,自己模拟了一下,发现了这个更新细节,特地写一篇文章来记录一下

1. 自己模拟效果

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
    <button id="add">add title</button>
    <button id="btn">change prop & update text</button>
    <script>
      const root = document.getElementById("root");
      const add = document.querySelector("#add");
      const btn = document.querySelector("#btn");
      let h1;
      let id = 1;
      const getTitle = () => `title${id++}`;
      add.addEventListener("click", function () {
        if(h1) return;
        h1 = document.createElement("h1");
        let title = getTitle()
        h1.setAttribute("id", title);
        h1.textContent = title;
        root.appendChild(h1);
      });
      btn.addEventListener("click", function () {
        if (!h1) return;
        let newTitle = getTitle();
        h1.setAttribute("id", newTitle);
        // h1.firstChild.nodeValue = newTitle // 不会导致h1节点发生变化 更新快
        // h1.firstChild.textContent = newTitle // 不会导致h1节点发生变化 更新快
        h1.firstChild.data = newTitle; // 不会导致h1节点发生变化 更新快
        // h1.textContent = newTitle // 会导致h1dom节点发生变化 慢
      });
    </script>
  </body>
</html>

如果使用前三种方式更新文本节点,你会发现插入的h1节点只会更新内容文本。 而如果直接使用h1.textContent进行更新,则会导致h1内部也会发生更新(具体更新什么我也不太清楚),有知道的小伙伴可以评论区留言,大家一起探讨,如下是我截取的gif

1. node.firstChild.nodeVlaue & node.firstChild.data & node.firstChild.textContent的表现行为

这种更新方式,可以看到h1标签并没有发生闪烁的情况

0811updateText03.gif

2. node.textContent的表现行为

这种更新方式,可以看到h1标签有明显的闪烁情况

0811updateText02.gif

2. react相关源码

setTextContent.js

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import {TEXT_NODE} from '../shared/HTMLNodeType';

/**
 * Set the textContent property of a node. For text updates, it's faster
 * to set the `nodeValue` of the Text node directly instead of using
 * `.textContent` which will remove the existing node and create a new one.
 *
 * @param {DOMElement} node
 * @param {string} text
 * @internal
 */
const setTextContent = function(node: Element, text: string): void {
  if (text) {
    const firstChild = node.firstChild;

    if (
      firstChild &&
      firstChild === node.lastChild &&
      firstChild.nodeType === TEXT_NODE
    ) {
      firstChild.nodeValue = text;
      return;
    }
  }
  node.textContent = text;
};

export default setTextContent;

3. 测速

然后笔者去google搜索了一下这几个相关api的方法,最后搜索到了可以测试这几个api速度的网址,这里我贴一下测速链接,下面的图片是我测试的结果

image.png

4. 总结

以前更新文本节点一直都是用的innerHTML/innerText/textContent这几个Api,却没注意到当中竟然还有这样的优化细节。 虽然知道这个可能没什么特别大的用处,毕竟现代浏览器不差这点性能,但是这让我看到了那些伟大的开源项目考虑到的细节,所谓见微知著,说的大概就是这种吧。 再次,向React官方致敬,也希望React能够发展的越来越好