vue2重点
Vue2响应式系统的工作原理
Vue2响应式系统的核心实现机制
Vue2的响应式系统基于数据劫持与发布-订阅模式,通过Object.defineProperty()
API拦截数据访问与修改,并结合依赖收集机制实现视图自动更新。其核心目标是建立数据与视图之间的联动关系,当数据变化时触发依赖组件的重新渲染。
数据劫持:对象与数组的响应式处理
对象响应式实现
初始化监听
Vue在初始化阶段通过observer
方法遍历data
对象,对每个属性调用defineReactive
函数,该函数内部使用Object.defineProperty()
定义属性的getter
和setter
。- getter:当属性被访问时触发,用于依赖收集(将当前Watcher添加到Dep中)。
- setter:当属性被修改时触发,用于派发更新(通知Dep中的所有Watcher执行更新)。
深度监听
若属性值为对象,defineReactive
会递归执行深度监听,直至属性为基本类型,确保嵌套对象的变化也能被捕获。
数组响应式实现
由于Object.defineProperty()
无法原生监听数组元素变化,Vue2通过重写数组原型方法解决:
- 拦截
push
、pop
、shift
、unshift
、splice
、sort
、reverse
共7个修改原数组的方法。 - 重写后的方法在执行时会触发依赖更新,并通过
dep.notify()
通知视图刷新。
依赖收集与派发更新流程
核心角色
- Observer:遍历数据对象,将所有属性转换为响应式。
- Dep(依赖收集器):每个响应式属性对应一个Dep实例,维护依赖该属性的Watcher列表。
- Watcher:观察者对象(如组件渲染Watcher、计算属性Watcher),负责接收数据变化通知并执行更新逻辑。
流程详解
依赖收集
组件初次渲染时,模板解析过程中访问数据属性会触发getter
,此时Dep.target
指向当前渲染Watcher,该Watcher被添加到属性对应的Dep中。派发更新
当数据通过setter
修改时,Dep会调用notify()
方法,遍历所有关联的Watcher并执行其update()
方法,最终触发组件重新渲染。
Vue2响应式系统的局限性与解决方案
局限性
问题场景 | 原因分析 |
---|---|
动态新增/删除对象属性 | Object.defineProperty() 仅初始化时劫持已有属性,新增/删除属性无法触发setter [[4]] |
直接修改数组索引或长度 | 重写的数组方法仅覆盖7个操作,直接修改索引(如this.arr[0] = 1 )无法被监听[[4]] |
深度监听性能开销 | 初始化时递归遍历所有嵌套对象,层级过深可能导致首屏渲染卡顿 |
解决方案
- 新增/删除属性:使用
Vue.set(obj, key, value)
或this.$set
添加属性,Vue.delete(obj, key)
删除属性[[4]]。 - 数组操作:优先使用重写后的数组方法(如
splice
),或直接替换数组(如this.arr = [...this.arr, newItem]
)。 - 性能优化:避免数据层级过深,或通过
Object.freeze()
冻结无需响应式的对象。
总结
Vue2通过Object.defineProperty()
结合发布-订阅模式实现响应式,核心在于数据劫持、依赖收集与派发更新的联动。尽管存在对新增属性、数组索引修改等场景的支持限制,但可通过Vue.set
等API规避。这一机制为Vue2的数据驱动视图提供了基础,而Vue3则通过Proxy API进一步优化了这些局限性。