一、Vue的响应式原理
Vue的响应式系统通过数据劫持和依赖追踪实现,核心流程如下:
-
数据劫持
• Vue 2.x:使用Object.defineProperty
递归遍历数据对象,将属性转换为getter/setter
,拦截属性的读取和修改操作。• Vue 3.x:改用
Proxy
代理对象,支持动态属性添加和数组变化监听,性能更优且覆盖场景更广。 -
依赖收集与触发
• 依赖收集(Getter触发):当组件渲染时访问数据属性,触发getter
,将当前组件对应的Watcher
存入Dep
(依赖收集器)。• 更新触发(Setter触发):当数据被修改时,触发
setter
,Dep
通知所有关联的Watcher
执行视图更新或副作用函数。 -
虚拟DOM与批量更新
数据变化后生成新的虚拟DOM树,通过Diff算法对比新旧树差异,仅更新必要的真实DOM节点,并通过异步队列(如nextTick
)批量处理更新任务,减少重排重绘。
二、getter
和setter
的触发时机
• Getter触发:
• 当读取响应式数据属性时触发(如模板渲染、计算属性计算、手动读取值)。
• 示例:{{ user.name }}
渲染时,会触发user.name
的getter
。
• Setter触发:
• 当修改响应式数据属性时触发(如用户输入、异步请求结果赋值)。
• 示例:this.user.name = 'Alice'
会触发setter
,通知所有依赖视图更新。
三、ref
与reactive
的区别及.value
的作用
-
设计目的
•ref
:用于基本数据类型(如number
、string
)和对象引用,通过Object.defineProperty
或Ref
对象包裹,确保响应性。•
reactive
:专用于对象/数组,基于Proxy
实现深层次响应式代理。 -
.value
的必要性
•ref
将基本类型包装为{ value: ... }
对象,访问时必须通过.value
获取内部值,因为基本类型无法直接被Proxy代理。•
reactive
直接代理对象,可直接访问属性(如state.count
),无需.value
。 -
基本类型与引用类型的核心差异
• 存储方式:基本类型值存储在栈内存,引用类型值存储在堆内存,变量存储的是引用地址。• 可变性:基本类型不可变(如
let a=1; a=2
是重新赋值),引用类型属性可变(如obj.name='Bob'
)。
四、Vue的Diff算法
-
核心策略
• 同层比较:仅比较同级节点,避免跨层级对比(复杂度从O(n³)降至O(n))。• 双端指针:从新旧子节点的头部和尾部同时向中间遍历,优先处理相同节点。
• Key优化:通过唯一
key
标识节点,减少不必要的DOM操作(如列表重排)。 -
具体步骤
• 对比节点类型/标