Vue实现这种数据双向绑定的效果,需要三大模块:
- Observer:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
- Compile:对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
- Watcher:作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
Observer
Observer的核心是通过Obeject.defineProperty()
来监听数据的变动,这个函数内部可以定义set
和get
,每当数据发生变化,就会触发set
。这时候Observer
就要通知订阅者,订阅者就是Watcher
。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
{{a}}
</body>
<script src="./mvvm.js"></script>
<script>
let vue = new Vue({
el:'#app',
data:{
a:{
a:1
}
}
})
console.log(vue)
</script>
</html>
new Vue的时候,会把vue的data属性进行递归,用Object.defineProperty()方法把这些属性全部转成set、get方法
当data中的某个属性被访问时,则会调用get方法,当data中的属性被改变时,则会调用set方法。
function Vue(options={}){
this.$options = options;//将所有属性挂载在options
var data = this._data = this.$options.data;
//观察者 递归处理传进来的data
observe(data)
//观察完之后,this代理了this._data 数据代理
for(let key in data){
Object.defineProperty(this,key,{
enumerable:true,
get(){
return this._data[key];//获取到新值
},
set(newVal){
this._data[key] = newVal//这里触发观察者【再次执行Observe(data)中的set方法】
}
})
}
}
//观察者 vm.$options
function Observe(data){//这里写我们的主要逻辑
for(let key in data){//把data属性通过object.defineProperty的方式定义
let val = data[key]
observe(val)//递归 使data的中的每个属性都被Object.defineProperty处理
Object.defineProperty(data,key,{
enumerable:true,
get(){
return val
},
set(newVal){//更改值得时候
if(newVal===val){//设置的值和以前是一样的东西
return
}
val = newVal//如果以后在获取值得时候,将刚才设置的值丢回去
observe(newVal)
}
})
}
}
function observe(data){
if (typeof data !== 'object'){
return
}
return new Observe(data)
}
①vue特点 不能新增不存在的属性
因为不存在的属性 没有 get和set,不能被监控
② 深度响应 因为每次赋予一个新对象时会给这个新对象增加数据劫持