Vue响应式:数据发生变化,会重新渲染页面
完成这个过程,我们需要:
- 侦查数据的变化
数据劫持 / 数据代理
- 收集视图依赖的数据
依赖收集
- 数据变化时,自动通知需要更新的视图部分进行更新
发布-订阅模式
一、如何侦查数据的变化 - 数据劫持
使用Object.defineProperty
和Proxy
:
- 数据模型是普通的js对象,把一个普通的js对象传入Vue实例当作data选项,Vue会遍历此对象的property,并使用Object.defineProperty把property全部转为getter/setter
- Vue通过设定对象属性的getter/setter方法来监听数据变化,通过getter进行依赖收集,每个setter方法就是一个观察者,数据变更时通知订阅者更新视图
二、为什么要收集视图依赖 - 依赖收集
通过收集依赖才能知道哪些地方依赖此数据,以及数据更新和派发更新。
核心思想:事件发布-订阅模式
重要角色:订阅者Dep和观察者Watcher
订阅者Dep
1. 为什么引入Dep
收集依赖、删除依赖和向依赖发送消息
2. 简单实现
class Dep{
constructor(){
// 保存watcher对象的数组
this.arr = []
}
// 在arr 数组中添加一个watcher对象
addArr(obj){
this.arr.push(obj)
}
// addArr方法:Dep对象中增加Watcher的订阅操作
// 通知所有watcher对象更新视图
notify(){
this.arr.forEach((obj)=>{obj.update()})
}
// 通知目前Dep对象的arr中的所有Watcher对象触发更新操作
}
当需要依赖收集的时候调用addArr方法,需要派发更新消息调用notify()
观察者Watcher
1. 为什么引入Watcher
当属性发生变化时,我们要通知用到此数据属性的地方,而使用此数据的地方很多且类型不一样,需要抽象出一个类集中处理这些情况,然后在依赖收集阶段只收集这个封装好的实例,通知也只需要通知这一个,然后它在负责通知其他地方
2. Watcher简单实现
在getter中收集依赖,在setter中触发依赖
当外界通过Watcher读取数据时,便会触发getter将Watcher添加至依赖中,将触发getter的Watcher收集到Dep中,当数据发生变化时,会循环依赖列表,遍历所有的Watcher
new Vue
创建vue实例后,Vue调用_init函数进行初始化,此时Data通过Observer转换为setter/getter形式,追踪数据的变化。当外界读取数据时触发getter函数,而当被赋值的时候执行setter函数- 当Render Function执行时,读取对象的值触发getter函数从而将Watcher添加至依赖中,进行依赖收集
- 修改对象值的时候,触发setter函数,setter通知之前收集依赖得到的订阅者Dep中的每一个Watcher,通知组件更新。此时Watcher开始调用update更新视图