https://zhuanlan.zhihu.com/p/69401061
![](https://img.haomeiwen.com/i7522654/e4b3fb7bd09564da.png)
import { h, init } from 'snabbdom'
// 1. hello world
// 参数:数组,模块
// 返回值:patch函数,作用对比两个vnode的差异更新到真实DOM
let patch = init([])
// 第一个参数:标签+选择器
// 第二个参数:如果是字符串的话就是标签中的内容
let vnode = h('div#container.cls', {
hook: {
init (vnode) {
console.log(vnode.elm)//undefined
},
create (emptyVnode, vnode) {
console.log(vnode.elm)//<div id="container" class="cls">Hello World</div>
}
}
}, 'Hello World')
let app = document.querySelector('#app')
// 第一个参数:可以是DOM元素,内部会把DOM元素转换成VNode
// 第二个参数:VNode
// 返回值:VNde
let oldVnode = patch(app, vnode)
// 假设的时刻
vnode = h('div', 'Hello Snabbdom')
patch(oldVnode, vnode)
![](https://img.haomeiwen.com/i7522654/05f7450e66a8f0cd.png)
- init函数
功能:init(modules, domApi),返回 patch() 函数(高阶函数)
为什么要使用高阶函数?
因为 patch() 函数再外部会调用多次,每次调用依赖一些参数,比如:
modules/domApi/cbs
通过高阶函数让 init() 内部形成闭包,返回的 patch() 可以访问到 modules/domApi/cbs,而
不需要重新创建
init() 在返回 patch() 之前,首先收集了所有模块中的钩子函数存储到 cbs 对象中
init内部最后return返回patch函数,把vnode渲染成真实的dom。并返回vnode,作为下次处理的旧节点 -
patch函数
patch函数
-
createElm 把虚拟Vnode转化为真实的dom
image.png
image.png
-
patchVnode
image.png
-
updateChildren
■ 功能:diff 算法的核心,对比新旧节点的 children,更新 DOM
■ 执行过程:
1.要对比两棵树的差异,我们可以取第一棵树的每一个节点依次和第二课树的每一个节点比较,但是这样的时间复杂度为 O(n^3),
2.在DOM 操作的时候我们很少很少会把一个父节点移动/更新到某一个子节点因此只需要找同级别的子节点依次比较,然后再找下一级别的节点比较,这样算法的时间复杂度为 O(n)。
3.在进行同级别节点比较的时候,首先会对新老节点数组的开始和结尾节点设置标记索引,遍历的过程中移动索引
4.在对开始和结束节点比较的时候,总共有四种情况
◇ oldStartVnode / newStartVnode (旧开始节点 / 新开始节点)
◇ oldEndVnode / newEndVnode (旧结束节点 / 新结束节点)
◇ oldStartVnode / oldEndVnode (旧开始节点 / 新结束节点)
◇ oldEndVnode / newStartVnode (旧结束节点 / 新开始节点)
四种
(oldStartVnode / newStartVnode ) || (oldEndVnode / newEndVnode)
oldEndVnode / newEndVnode
oldEndVnode / newStartVnode
查找key
循环结束
网友评论