起因
笔者在研究React中DOM-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标签并没有发生闪烁的情况
2. node.textContent的表现行为
这种更新方式,可以看到h1标签
有明显的闪烁情况
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速度的网址,这里我贴一下测速链接,下面的图片是我测试的结果
4. 总结
以前更新文本节点一直都是用的innerHTML/innerText/textContent这几个Api,却没注意到当中竟然还有这样的优化细节。
虽然知道这个可能没什么特别大的用处,毕竟现代浏览器不差这点性能,但是这让我看到了那些伟大的开源项目考虑到的细节,所谓见微知著,说的大概就是这种吧。
再次,向React官方致敬,也希望React能够发展的越来越好