Libx

理解Vue数据绑定

Word count: 560Reading time: 2 min
2018/08/05 Share

基本原理

Vue 实现数据绑定的方法是采用数据劫持解和发布者-订阅者的方法,通过 Objec.defineProperty()来劫持各个属性的 setter 和 getter 在数据变动的时候,触发相应的监听回调。

思路

  1. 实现一个监听器 Observer,对数据对象的所有属性进行监听,如有变动拿到新值并且通知订阅者
  2. 实现一个指令解析器,对各个元素节点的指令进行扫描解析,根据指令模板替换数据,以及绑定相应函数
  3. 实现一个 Watcher 连接 Observer 和 Compile 的桥梁,订阅并受到每个属性变动的通知,执行绑定的相应回调函数,更新视图
  4. mvvm 入口函数

实现

Observer

这里通过 Object.defineProperty()来给属性添加 getter 和 setter(其实这里可以使用 proxy 来实现)

let data = {name:"xiaoming"}
observe(data)
data.name = "faker" //
function observe(data){
if(!data||typeof data!=='object'){return;}
Object.keys(data).forEach(
(key)=>{defineReactive(data,key,data[key])}
)
}
function defineReactive(data,key,val){
object(val);
Object.defineProperty(data,key,{
enumerable:true,
configurable:false,
get:function (){return val},
set:function (newVal){
console.log(`${val}---->${newVal}`)
val = newVal
}
})
}

现在已经实现了监听数据变化,之后便是如何通知订阅者,接下来要实现一个消息订阅器,我们这里维护一个数组来收集订阅者,数据变动触发 notify,再调用订阅者的 update 方法:

function defineReactive(data,key,val){
let dep = new Dep()
object(val);
Object.defineProperty(data,key,{
enumerable:true,
configurable:false,
get:function (){return val},
set:function (newVal){
console.log(`${val}---->${newVal}`)
val = newVal
dep.notify()
}
})
}
function Dep(){
this.subs = []
}
Dep.prototype.addSub = function (sub){
this.subs.push(sub)
}
Dep.prototype.notify = function (){
this.subs.foreach((sub)=>{sub.update()})
}

接下来就是怎么向订阅器添加订阅者,上面的思路整理中我们已经明确订阅者应该是 Watcher, 而且 var dep = new Dep();是在 defineReactive 方法内部定义的,所以想通过 dep 添加订阅者,就必须要在闭包内操作,所以我们可以在 getter 里面动手脚:

Object.defineProperty(data,key,{
get:function (){
// 由于需要在闭包内添加watcher,所以通过Dep定义一个全局target属性,暂存watcher, 添加完移除
Dep.target&&dep.addDep(Dep.target)
return val
},
})
// watcher.js
Watcher.prototype={
get:function(key){
Dep.target = this
this.value = data[key]
Dep.target = null
}
}
CATALOG
  1. 1. 基本原理
  2. 2. 思路
  3. 3. 实现
    1. 3.1. Observer