Vue 如何实现响应式原理
Vue 将MVVM做为数据绑定的入口。
几个Vue源码关键核心概念:
- Observer 数据的观察者,监听自己的model数据变化
- Complie 解析编译模板指令
- Watcher 数据订阅者,是Observer 与 Complie 之间通信的桥梁(用来订阅变化时执行相应操作的)。
进一步分析
双向绑定流程:
Observer --data update–> Dep --notify通知–> Watcher --update–> View
-------------------------------Dep----<–subscribe订阅—Watcher--------------------
Dep 是Observer 和 Watcher 之间的桥梁, Observer 观察到数据变化则告知 Dep, Dep会向收集到的Watcher 订阅者们(Dep 内会有一个数组用来收集依赖的订阅者),发送数据改变的通知,收到通知后进行View更新。*
一个简单的实现Demo实例:
class Vue{
constructor (options) {
this.$options = options
this._data = options.data
observer(options.data, this._update.bind(this))
this._update()
}
_update() {
this.$options.render()
}
}
function observer(obj, cb) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key], cb)
})
}
function defineReactive(obj, key, val, cb) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=> {
console.log(`访问了: ${key}`)
return val
},
set: newVal => {
if(newValue === val)
return
console.log(`设置了: ${key}`)
val = newVal
cb()
}
})
}
var vm = new Vue({
el: '#app',
data: {
text: "vm data attr"
},
render(){
console.log('runder function run.....')
}
})
vue 如何通过Dep 避免渲染无关值更新导致的渲染问题:
vue 会在Render 函数中,收集本次渲染相关的值,并处理相关值为响应式。 所以,与渲染无关的值,并不会触发get, 也就不会在依赖收集器中添加到监听。 所以渲染无关的值,即使调用set赋值, notify中的subs也是空的。 因此不会触发渲染。
注: 这里“依赖收集器” 并非官网定义的名词。 只是个人理解起名。
总结:
- vue将data 初始化为一个Observer 并将对象中的每个值,重写其get, set, data中的每个key, 都有一个独立的依赖收集器;
- 在get中,向依赖收集器添加了监听;
- 在mount 时, 实例了一个Watcher, 将收集器的目标指向了当前Watcher;
- 在data值发生变更时,触发set, 触发了依赖收集器中的所有监听的更新, 从而触发Watcher.update