面试了许多求职者,很多求职者只知其然,而不知所以然,写此文章为广大前端小伙伴指引迷津。纯干货!纯干货!纯干货!
目录
Virtual DOM
什么是Virtual DOM?
答:将真实的DOM的数据抽取出来,以对象的形式模拟树形结构。
Virtual DOM的作用?
答:避免昂贵的DOM操作与大量无谓的计算量,通过diff算法,一边比较新旧节点一边给真实的DOM打补丁。
diff的原理?
1.找到对应的真实dom,称为`el`
2.父级进行比较更新
3.如果两者都有子节点,则执行`updateChildren`函数比较子节点,
updateChildren如下:
- 将
Vnode的子节点Vch和oldVnode的子节点oldCh提取出来 oldCh和vCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和vCh至少有一个已经遍历完了,就会结束比较。
现在分别对oldS、oldE、S、E两两做sameVnode比较,有四种比较方式,当其中两个能匹配上那么真实dom中的相应节点会移到Vnode相应的位置,这句话有点绕,打个比方
- 如果是oldS和E匹配上了,那么真实dom中的第一个节点会移到最后
- 如果是oldE和S匹配上了,那么真实dom中的最后一个节点会移到最前,匹配上的两个指针向中间移动
- 如果四种匹配没有一对是成功的,分为两种情况
- 如果新旧子节点都存在key,那么会根据
oldChild的key生成一张hash表,用S的key与hash表做匹配,匹配成功就判断S和匹配节点是否为sameNode,如果是,就在真实dom中将成功的节点移到最前面,否则,将S生成对应的节点插入到dom中对应的oldS位置,S指针向中间移动,被匹配old中的节点置为null。 - 如果没有key,则直接将
S生成新的节点插入真实DOM(ps:这下可以解释为什么v-for的时候需要设置key了,如果没有key那么就只会做四种匹配,就算指针中间有可复用的节点都不能被复用了)
diff的比较方式?
答:前端操作dom的时候了,不会把当前元素作为上一级元素或下一级元素,很少会跨越层级地移动DOM元素,在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较,因此复杂度的问题转换成 O(n) 复杂度的问题。
diff的弊端?
答:相同⽗元素的⼦元素必须有独特的 key,否则重复的 key 会造成渲染错误。
Vue vs React
- 相同点
- 都是两组虚拟dom的对比(react16.8之后是fiber与虚拟dom的对比)
- 只对同级节点进行对比,简化了算法复杂度
- 都用key做为唯一标识,进行查找,只有key和标签类型相同时才会复用老节点
- 遍历前都会根据老的节点构建一个map,方便根据key快速查找
- 不同点
- react会自顶向下全diff.
- vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
- react在diff遍历的时候,只对需要修改的节点进行了记录,形成effect list,最后才会根据effect list 进行真实dom的修改,修改时先删除,然后更新与移动,最后插入
- vue 在遍历的时候就用真实dom
insertBefore方法,修改了真实dom,最后做的删除操作 - react 采用单指针从左向右进行遍历
- vue采用双指针,从两头向中间进行遍历
- react的虚拟diff比较简单,vue中做了一些优化处理,相对复杂,但效率更高