VDOM
为什么使用 VDOM?
- react 和 vue:数据驱动视图
- 直接操作 dom 非常消耗性能;
- 计算次数减少比较困难,转移为 JS计算;
- VDOM:使用 JS 模拟 DOM 结构,计算出最小变量,操作 DOM;
- VDOM 是目前最合适「数据驱动视图」的技术方案;
用 JS 模拟 DOM 结构
<div className={style.container} id="div1">
<p>拙慕</p>
<ul style={{fontSize:20}}>
<li>li</li>
</ul>
</div>
对应的模拟出来
{
tag: 'div',
props: {
className: 'container',
id: 'div1'
}
children: [
{
tag: 'p',
childred: '拙慕'
},
{
tag: 'ul'
props: {
style: 'font-size:20',
},
children: [{
tag: 'li',
children: 'lili'
}]
}
]
}
通过 snabbdom 学习 VDOM
核心内容: 以 VDOM(VNODE)描述真实DOM
- h()创建节点
- init设置模块,生成patch函数
- patch对比dom差异,将差异更新的到dom中
官方文档:https://github.com/snabbdom/snabbdom
中文文档:https://github.com/coconilu/Blog/issues/152
h 函数
用法:h(<标签>,<属性>,[子元素])
var h = require('snabbdom/h').default;
var vnode = h('div', {style: {color: '#000'}}, [
h('h1', 'Headline'),
h('p', 'A paragraph'),
]);
转换为DOM:
<div style={{ color: '#000' }}>
<h1>Headline</h1>
<p>A paragraph</p>
</div>
patch函数
用法:path(oldNode,newNode);
patch:即补丁函数。snabbdom的核心函数,可以理解为对新旧dom进行差异化的函数,判断 key 和 selector(tag) 都相等,则判断为相同元素,会返回一个新的虚拟节点。
// key :undefined
undefined === undefined // true
总结
- 用 JS 模拟 DOM结构(VNODE);
- 新旧 VNODE 对比,得出最小的更新范围,最后更新 VDOM;
- 数据驱动视图模式下,有效控制 DOM 操作
diff
关于 diff
diff 一种对比算法,一种广泛的概念,不是 react ,vue 独有的;
tree diff 算法的时间复杂度是 O(n^3),不可用的,优化后才能用,即:
- 只比较统计,不跨级比较;
- tag 不相同,直接删除重建,不再进行深度比较;
- tag(selector)两者都相同,则相同,不再进行深度比较;
优化后时间复杂度变为可用的O(n)。
diff算法流程
pathcVnode
- vnode.text === undefined,vnode.children 一般都有值;
- 对比新旧节点,都有 children,进行两个 children 之间的对比;
- 新 children 有,旧 children 没有,则添加 新children,清空旧的 text;
- 旧 children 有值,新 children 没值,移除 旧 children,添加新 text;
- 旧 text 有值,新的 text 没值,移除 旧 text;
updateChildren 进行 key 的对比
- 开始 vs 开始
- 结束 vs 结束
- 开始 vs 结束
- 结束 vs 开始
- 当前新节点 与每个旧节点进行对比
- 找到key 相同,则 判断 tag 是否相同
新增、移除 vnode
- 新增 :addVnodes()
- 移除:removeVnodes()
上述步骤只有在前一步都没命中才会继续执行下一步;
JSX 本质
通过 bable 在线编辑器查看:babeljs.cn
v16.x及以前版本
h 函数即 React.createElement()
v17.x及之后
h 函数即 jsx(),注意这里为了避免重名使用:_jsx();
总结
- React v16.x 及以前 babel 进行 jsx 解析编译的是根据加粗样式 @babel/babel-preset-react-app 解析成React.createElement进行包裹;
- React v17.x 及之后的版本,对 jsx 转换用react/jsx-runtime,而不再依赖React.createElement;
- 无论是 React.createElement 还是 react/jsx-runtime,都是一个 h函数,第一个参数可以是 组件,也可以是 html tag。
React 和 Vue 循环中为什么强调使用 key?
- VDOM diff 算法会根据 key 判断是都要删除元素
- 匹配到 key,判断 tag 是否相同,直接移动即可,性能好;
- 未匹配到,则直接删除重建;
附上本人用脚画的草图: