Vue2响应式原理
- 劫持数据属性,完成数据响应式
- 获取数据时,完成依赖收集工作
- 修改数据时,完成派发更新操作
数据劫持-Observer
vue 会遍历data中的属性,通过Object.defineProperty()给每个属性添加get和set方法
javascript
data(){
return {
name:'john',
info:{
school:'xxx'
}
}
}
Object.defineProperty('name',{
get(){
// 获取时运行
}
set(value){
// 修改时运行
}
})
// 针对info会深度遍历
后续,动态的添加和删除属性是无法检测的
arduino
info.age = '18' // 检测不到
delete info.school // 检测不到
但vue提供了set和delete实现上述的检测
kotlin
this.$set(this.info,age,'18')
this.$delete(this.info,school)
针对数组,vue修改了它的隐式原型
ini
[1,2,3]._proto_ = defVue // Vue 自定义对象
defVue._proto_ = Array.prototype
数组存在缺陷
- 检测不到 数组长度的变化
- 检测不到根据索引修改元素
依赖收集-Dep
在数据劫持的过程中,vue会为响应式对象中的每个属性,对象本身,数组本身创建一个Dep实例,Dep实例可以完成记录依赖以及派发更新的工作,当读取响应式对象的某个属性时,进行依赖的收集depend()
具体依赖收集过程-Watcher
Vue在初始化时,会为每一个组件实例创建一个watcher,watcher中记录了该组件的render函数,首次挂载时,即在mounted阶段,watcher会先运行一次render函数,完成依赖的收集(将用到的响应式数据与watcher进行关联)
scss
new Watcher(render)
render(){
// this.a
// 调用get
// get调用时,dep.depend() 记录wather
}
派发更新-Update
当改变某个属性时,进行派发更新notify()
kotlin
// 数据修改
this.a = '1'
// 调用set
// set调用时,dep.notify() 通知watcher运行render函数,界面重新渲染并重新记录当前依赖
更新过程-Scheduler
watcher 收到派发更新通知后,并非立即执行对应的函数,而是把自己交给一个叫做调度器的东西,由其完成,调度器维护一个执行队列,该队列同一个watcher仅会存在一次,队列中的watcher会被调度器通过nextTick函数放到事件循环的微任务队列
scss
nextTick(){
Promise.resolve().then(watcher)
}