VUE
# VUE
# 响应式数据原理
数组和对象类型当值变化时如何劫持到,对象内部通过defineReactive
方法,使用Object.defineProperty
将属性进行劫持(只会劫持已经存在的属性),数组通过重写数组来实现
vue3
使用proxy来实现响应式原理
# 实现的核心类:
Observer:给对象属性添加getter和setter,用于以来收集和派发更新
Dep:用于手机当前响应式对象的依赖关系,每个响应式对象包括子对象都会有一个Dep实例,当数据变更时通过dep.notify()
通知各个watcher
Watcher:观察者对象,实例分为渲染watcher,计算属性watcher,监听器watcher
# 原理
当创建Vue实例时,vue会遍历data选项的属性,利用Object.defineProperty
为属性添加getter和setter对数据的读取进行劫持(getter用来收集依赖,setter用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化
每个组件实例会有相应的watcher实例,会在组件渲染的过程中记录依赖的数据属性,当依赖项被改动时,setter方法会通知依赖与data的watcher实例重新计算(派发更新),从而使关联的组件重新渲染
vue.js采用数据劫持结合发布订阅模式,通过object.defineProperty
来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调
# Computed和Watch
# 区别
computed计算属性:依赖其它属性值,并且computed的值有缓存,只有依赖的属性值发生改变,下一次获取computed的值时才会重新计算computed的值
watch监听器:更多的是【观察】的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据发生变换时都会执行回调进行后续的操作
# 运用场景
当我们需要进行数值的计算,并且依赖于其他数据时,应该使用computed,利用computed的缓存特性,避免每次获取值时,都要重新计算;当我们需要在数据变化时执行异步或者开销比较大的操作时,应该使用watch,watch允许执行异步操作,限制我们执行该操作的频率
# 为什么Vue3使用proxy,抛弃了object.defineProperty
Object.defineProperty
只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 2.x 里,是通过 递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择。Proxy 可以劫持整个对象,并返回一个新的对象。Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
# Vue中的key到底有什么作用
key是vnode的唯一id,依靠key,我们的diff操作可以更加准确,更加快速。diff算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的key与旧节点进行对比,从而找到相应的旧节点。
# nextTick原理
# JS 运行机制
JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:
- 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
- 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
- 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
- 主线程不断重复上面的第三步。
# 原理
- vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行
- microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕
- 考虑兼容问题,vue 做了 microtask 向 macrotask 的降级方案
# Data为什么必须是函数
因为组件是可以复用的,JS 里对象是引用关系,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染,产生副作用。
所以一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。new Vue 的实例是不会被复用的,因此不存在以上问题。