Vue面试题虚拟dom和key
Posted 重温新知
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue面试题虚拟dom和key相关的知识,希望对你有一定的参考价值。
本文收集和整理了有关虚拟dom和key的面试题,希望对准备面试和深入学习的小伙伴有所帮助!
注:文中题目不是原创,但为了添加为话题标为原创。题目选于多篇文章,又重新整理、整合、排序,再此一并感谢。收集材料时有的题目忽略了标注来源,如原作者希望标出来源,请联系小编。
1.谈一下对虚拟DOM的理解?虚拟DOM主要做了什么?虚拟DOM本身是什么?
什么是虚拟DOM?
从本质上来说,Virtual Dom是一个JS对象,通过对象的方式来表示DOM结构。
将页面的状态抽象为JS对象的形式,配合不同的渲染工具,使跨平台渲染成为可能。
通过事务处理机制,将多次DOM修改的结果一次性的更新到页面上,从而有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能。
虚拟dom是对DOM的抽象,这个对象是更加轻量级的对DOM的描述。它设计的最初目的,就是更好的跨平台,比如node.js就没有DOM,如果想实现SSR,那么一个方式就是借助虚拟dom,因为虚拟dom本身就是js对象。
在代码渲染到页面之前,vue或者react会把代码转换成一个对象(虚拟DOM)。以对象的形式来描述真实dom结构,最终渲染到页面。
在每次数据发生变化前,虚拟dom都会缓存一份,变化之时,现在的虚拟dom会与缓存的虚拟dom进行比较。
在vue或者react内部封装了diff算法,通过这个算法来进行比较,渲染时修改改变的变化,原先没有发生改变的通过原先的数据进行渲染。
另外现代前端框架的一个基本要求就是无需手动操作DOM,一方面是因为手动操作DOM无法保证程序性能,多人协作的项目中如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动DOM操作可以大大提升开发效率
为什么要用Virtual Dom?
1.保证性能下限,再不进行手动优化的情况下,提供能过得去的性能
看一下页面渲染的一个过程
解析html --- 生成DOM --- 生成cssDom -- Layout -- Paint -- Compiler
下面对比一下修改DOM时真实DOM操作和Virtual Dom的过程,来看一下它们重排重绘的性能消耗
真实DOM:生成HTML字符串 + 重建所有的DOM元素
Virtual Dom:生成VNode + DOMDiff + 必要的dom更新
Virtual Dom的更新DOM的准备工作耗费更多的时间,也就是js层面,相对于更多的DOM操作它的消费是极其便宜的。尤大大曾说到:框架给你的保证时,你不需要手动优化的情况下,我依然可以给你提供过得去的性能
2.跨平台
Virtual Dom本质上是JS对象,可以很方便的跨平台操作,比如服务端渲染、uniapp等
Virtual Dom真的比真实DOM性能好么?
1.首次渲染大量DOM时,由于多了一层虚拟DOM的计算,比innerHTML插入慢
2.正如它能保证性能下限,在真实DOM操作的时候进行针对性的优化时,还是更快的。
2、再说一下虚拟Dom以及key属性的作用
由于在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生一定的性能问题。这就是虚拟Dom的产生原因。
Vue2的Virtual DOM借鉴了开源库snabbdom的实现。
Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象。(也就是源码中的VNode类,它定义在src/core/vdom/vnode.js中。)
VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段。
key的作用是尽可能的复用 DOM 元素。
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识。
3、Vue 中的 key 有什么作用?
Vue 中 key 的作用是:key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速。
更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key
对比中可以避免就地复用的情况。所以会更加准确。
更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key;
const map = {};
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}
或者说:需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。作用主要是为了高效的更新虚拟DOM。
4、Vue 中 key 的作用(源码理解)
看是不错的回答:如果不加 key,那么 vue 会选择复用节点(Vue 的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的 bug
不加 key 也不一定就会复用,关于 diff 和 key 的使用,建议大家还是找一些质量好的文章真正深入的看一下原理。
文章如下:
https://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247483676&idx=1&sn=56885c423ec7700c499356c86189ba3d&chksm=eb043965dc73b073745701d2e92bf32629cb1c096e781e8ff728b9adc36d4c7b5a04ebe6dcea&token=1208709646&lang=zh_CN&scene=21#wechat_redirect
5、diff算法的时间复杂度?(源码理解)
看似不错的回答:两个数的完全的 diff 算法是一个时间复杂度为 o(n3), Vue 进行了优化 O(n3)复杂度的问题转换成 O(n)复杂度的问题(只比较同级不考虑跨级问题)
在前端当中,你很少会跨级层级地移动 Dom 元素,所以 Virtual Dom 只会对同一个层级的元素进行对比
但实际:听这个描述来说,React 没有对 O(n3) 的复杂度进行优化?事实上 React 和 Vue 都只会对 tag 相同的同级节点进行 diff,如果不同则直接销毁重建,都是 O(n) 的复杂度。
6、Vue2.x和Vue3.x渲染器的diff算法分别说一下
简单来说,diff算法有以下过程
先同级比较再比较子节点
先判断一方有子节点和一方没有子节点的情况。如果新的一方有子节点,旧的一方没有,相当于新的子节点替代了原来没有的节点;同理,如果新的一方没有子节点,旧的一方有,相当于要把老的节点删除。
再来比较都有子节点的情况,这里是diff的核心。首先会通过判断两个节点的key、tag、isComment、data同时定义或不定义以及当标签类型为input的时候type相不相同来确定两个节点是不是相同的节点,如果不是的话就将新节点替换旧节点。
如果是相同节点的话才会进入到patchVNode阶段。在这个阶段核心是采用双端比较的算法,同时从新旧节点的两端进行比较,在这个过程中,会用到模版编译时的静态标记配合key来跳过对比静态节点,如果不是的话再进行其它的比较。
Vue3.x借鉴了ivi算法和 inferno算法
在创建VNode时就确定其类型,以及在mount/patch的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看。)
该算法中还运用了动态规划的思想求解最长递归子序列
以上是关于Vue面试题虚拟dom和key的主要内容,如果未能解决你的问题,请参考以下文章
面试中的网红Vue源码解析之虚拟DOM,你知多少呢?深入解读diff算法
前端技能树,面试复习第 27 天—— React Diff 算法的原理,和 Vue 有什么区别 | 虚拟 DOM | key 的原理,为什么要用