我正在参加「掘金·启航计划」
我们用最简单的方式一步一步实现vue核心吧
为什么需要 diff?
本质上就是为了性能,性能,性能,但是这里还是需要知道为啥 dom 操作容易慢,注意,不是说 dom 操作慢... *
- 数据模型 -> virtual dom -> 视图(DOM)
- f(state) -> View
why O(n)?
严格意义不是真的O(n),复杂度其实是O(nm)
how O(n)?
同层级比较 react 是怎么设计将复杂度砍下来呢?其实就是在算法复杂度、虚拟 dom 渲染机制、性能中找了一个平衡,react 采用了启发式的算法,做了如下最优假设:
- a. 如果节点类型相同,那么以该节点为根节点的 tree 结构,大概率是相同的,所以如果类型不同,可以直接「删除」原节点,「插入」新节点
- b. 跨层级移动子 tree 结构的情况比较少见,或者可以培养用户使用习惯来规避这种情况,遇到这种情况同样是采用先「删除」再「插入」的方式,这样就避免了跨层级移动
- c. 同一层级的子元素,可以通过 key 来缓存实例,然后根据算法采取「插入」「删除」「移动」的操作,尽量复用,减少性能开销
- d. 完全相同的节点,其虚拟 dom 也是完全一致的
- 基于这些假设,可以将 diff 抽象成只需要做同层比较的算法,这样复杂度就直线降低了
用index做key
vue: [0,1,2],[0,1] 误删 react: 会重新渲染 都是新建
虚拟dom过程
虚拟 DOM
- 什么是虚拟 DOM
{
type: 'div',
props: {
children: []
},
el: xxxx
}
- 怎么创建虚拟 DOM
-> h 、createElement...
function h(type, props) { return { type, props } }
- 使用呢
JSX:
<div>
<ul className='padding-20'>
<li key='li-01'>this is li 01</li>
</ul>
</div>
经过一些工具转一下:
createElement('div', {
children: [
createElement('ul', { className: 'padding-20' },
createElement('li', { key: 'li-01'}, 'this is li 01'))
]
})
- 虚拟DOM的数据结构有了,那就是渲染了 (mount/render)
f(vnode) -> view
f(vode) {
document.createElement();
....
parent.insert()
. insertBefore
}
export const render = (vnode, parent) => { }
<div id='app'></div>
- diff 相关了(patch)
f(oldVnodeTree, newVnodeTree, parent) -> 调度? -> view