vue的实现八股
双向绑定原理
Vue的双向绑定原理是通过数据劫持和观察者模式实现的。
vue使用了响应式的对象,即当数据发生改变的时候,视图也会随之改变
数据劫持:
vue2使用了object.definedproperty对数据的每个属性进行劫持,从而逐一对每个属性进行set或者get操作,对于一些动态操作和数组操作需要额外的处理
vue3使用了proxy来绑定整个对象,会拦截对象的每个新增,删除等操作,更加的快捷。
vue会绑定一个watcher对象来监听数据的变化,当数据变化时就会进行相应的视图更新
观察者模式(非父子组件之间的通信才是发布订阅模式,注意区别):
vue中有一个watcher对象,用于观察数据的变化以及收集依赖
当vue双向绑定一个数据时,会创建一个watcher对象,并且将watcher对象收集到相关依赖中,当数据发生改变的时候,就会通知所有watch对象,并随着发生相应的改变
vue2数组响应式是怎么实现的呢
vue2中的由于数据劫持是把对象内的每一个属性进行分别绑定,因此当数组元素新增或者删除时,实际变化的是数组length属性,而并没有对每个属性改变,因此数组无法捕获到变化
vue2数组的响应式是通过劫持数组的方法来实现,例如push,pop,shift,sort,reverse等方法,他们在完成对数组操作的同时,还会发生相应通知给watch对象,实现相关的更新操作。
因此在vue2对数组进行操作的时候,尽量使用数组原生方法来操作,如果使用索引对数组进行改变是无法实现响应式的
this.arr[0] = 'd'; // 直接通过索引修改数组元素
this.arr.length = 2; // 直接修改数组的长度
//无法实现响应式
可以使用Vue的$set方法来修改数组元素
$set是Vue中的一个实例方法,用于在响应式对象上添加响应式属性
渲染的过程
1.模板的编译渲染
2.数据的双向绑定
3.创建虚拟dom:vue会根据模板和数据生成虚拟dom,它可以代替真实的dom进行操 作,并将最终结果反应到真实dom上
4.diff算法更新:通过比较vnode的变化,找出需要改变的地方,实现局部的响应式更新
5.最终的结果反应到真实dom上,并且更新相关的节点,随后完成渲染
diff算法过程
diff算法有两个重要策略:
1.比较属性,先对属性比较,属性不同才会更新,同时对子节点进行递归遍历比较
2.key标识,通过唯一的key标识来比较节点
diff算法本质在于,使用双指针对比新旧dom树,然后对子节点进行递归比较:
1.比较根节点,如果根节点不同,直接替换dom树
2.根节点相同则比较他们的子节点
3.比较key值,如果不同,替换。
4.如果key相同,则比较子节点的属性
5.如果key和子节点的属性都相同,则以此类推继续向下递归遍历
注意!即使key相同,属性也可能因为绑定了新的事件或者样式等发生改变,所以key比较完之后,还要对属性进行比较
浏览器为什么不引入虚拟dom
1.不一定所有的业务场景都需要虚拟dom的局部更新功能,一些简单的操作,直接对真实dom进行操作就可以了
2.虚拟dom虽然可以做到更好的视图更新,但是也会带来额外的开销,虚拟dom的创建的和比较,diff算法,都会有额外的性能开销
3.浏览器之间的兼容性问题,不同的浏览器对浏览器引擎的实现可能有所不同,这可能导致在使用虚拟 DOM 时出现一些兼容性问题
vue3的diff算法优化
1.静态标记:对于一些不会变化的静态文本等元素,vue3可以对他们进行标记,对他们进行跳过,减少diff算法的时间
2.模板的优化:对于一些循环渲染,条件渲染,vue3会对他们没有变化的部分进行标记,下次更新对比的时候就可以直接跳过他们
3.fragment支持:vue2的根节点只能有一个,而vue3可以有多个根节点,可以在diff算法避免一些不需要的更新操作,因为有多个根节点,diff算法可以对多个根节点进行比较更新,而vue2只有一个根节点,需要递归到子节点比较
4.静态提升:在编译阶段就对静态内容进行提前创建,避免渲染过程的重复创建,减少运行开销
真实dom是异步更新
为了减少对真实dom的操作引起回流重绘等问题,通过diff算法对虚拟dom进行比较,并且把每个修改操作推入到微任务队列中,通过异步更新的方式,在下一次事件循环中把这些修改一次性应用到真实dom上
通俗的说,就是vue data中的数据更新时,它不会立即去渲染页面,也就是修改页面中的dom,而是先存储起来,如果一个数据被改了很多次,以最后一次修改为准,这就是所谓的异步更新DOM
异步更新可以保证所有的虚拟dom更新完成之后才会应用到真实dom上,从而减少了对真实dom操作
异步更新还确保了所有的虚拟DOM更新完成后才会应用到真实DOM上。这样可以避免因为数据更新导致的页面抖动或者闪烁等问题。保证修改的同时,整个页面的稳定性和一致性。
$nextTick的触发
每次数据变化都会将相应的DOM更新操作推入到异步更新队列中,这些操作会被放入微任务队列中,随后Vue会执行微任务队列中的回调函数,也就是$nextTick的回调函数。
$nextTick 方法可以确保在下一次DOM更新完成之后执行回调函数。虽然DOM的更新并没有立即应用到真实DOM上,但由于 $nextTick 的回调函数是在微任务中执行的,所以此时已经可以获取到更新后的DOM元素的内容了
以下几种异步操作可能会对DOM进行进一步的改变:
- AJAX请求:在进行异步的API请求或数据获取时,可能会根据返回的结果再次修改DOM。
- 定时器:使用setTimeout或setInterval等方法设置的定时器,可以在一段时间后对DOM进行修改。
- 事件监听器:当触发一些事件时,例如点击事件、滚动事件或键盘事件等,可能会导致DOM的改变。
- Vue的生命周期钩子函数:在Vue组件的生命周期中,存在一些钩子函数,例如created、mounted等,在这些钩子函数中,可以对DOM进行进一步的操作
promise为什么能实现nextTick呢
因为promise本质也是一个异步任务,他的回调在微任务队列中,和nextTick的执行一样,本质就是有一个延后性,使得真实dom发生改变后可以获取到当前dom元素,因此使用setTimeout()模拟也是同理
Vue 2.x 中使用 setTimeout() 模拟微任务延迟执行存在一定的延迟性,可能不够精确。
在 Vue 3.x 中,Vue 则采用了原生的 Promise 和 queueMicrotask() 来实现更精确的微任务延迟执行。
原文地址:https://blog.csdn.net/m0_58118986/article/details/137926468
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!