一、更新机制。
参考帅气的别人家博主:更新机制!
二、数据绑定
原理:
vue主要是借助对象的访问器属性(Object.defineProperty)劫持数据,并结合订阅者-发布者模式来实现数据双向绑定。
通过Object.defineProperty把data中的各数据属性改为访问器属性,来劫持每个属性的setter、getter;setter劫持到数据变化后,作为发布者 发布通知,订阅者们接到通知后更新数据。
为了进一步说明vue原理,这里我们借助实例来进行讲解。
实例:
有一段html代码(mvvm.html),需要保持 输入框中数据和data中数据同步。需求有了,那我们如何实现呢?看代码:
mvvm.html代码:
<div id="app">
<input type="text" id="a" v-model='name' />
{{name}}
</div>
利用Object.defineProperty把data中的各数据属性改为访问器属性:
//把data中的所有数据属性修改为访问器属性:
function observe(obj,vm){
Object.keys(obj).forEach(function(key){
defineReactive(obj,key,obj[key]);
})
}
//数据属性修改为访问器属性:
function defineReactive(obj,key,val){
var dep = new Dep();//主题对象,存放key属性的所有订阅者
Object.defineProperty(obj,key,{
get: function(){
if(Dep.target){
dep.subs.push(Dep.target);//添加订阅者
}
return val;
},
set: function(newVal){
if(val==newVal)return;
val = newVal;//数据劫持:当给key属性赋值时,会触发set方法
dep.notify();//发布通知
}
})
}
定义主题对象:添加订阅者和发布者方法
function Dep(){
this.subs = [];
}
Dep.prototype = {
addSub: function(sub){//添加订阅者
this.subs.push(sub);
},
nodify: function(){//发布通知
this.subs.forEach(function(sub){
sub.update();//订阅者更新数据
})
}
}
订阅者:添加数据更新方法
function Watcher(vm, node, name){
Dep.target = this;
this.name = name;
this.node = node;
this.vm = vm;
this.update();
Dep.target = null;
}
Watcher.prototype = {
update: function(){//更新数据
this.get();
this.node.nodeValue = this.value;
},
get: function(){//获取数据
this.value = this.vm.data[this.name];//触发get
}
}
利用documentFragment(文档片段)来处理节点,处理后再把文档片段插入挂载目标(注:操作documentFragment优于直接操作Dom节)
function nodeToFragment(node,vm){
var fragment = document.createDocumentFragment();
var child;
while(child = node.firstChild){
compile(child,vm);//编译数据
fragment.appendChild(child);
}
return fragment;
}
编译数据:解析模板指令,将模板中的变量替换成数据
function compile(node,vm){
var reg = /\{\{(.*)\}\}/;
if(node.nodeType==1){
var attrs = node.attributes;
for(var i=0,ln=attrs.length;i<ln;i++){
if(attrs[i].nodeName=='v-model'){
var name = attrs[i].nodeValue;
node.addEventListener("input",function(event){
//给相应的data赋值,触发该属性的set方法
vm.data[name]=event.target.value;
})
node.value = vm.data[name];//将data的值赋值给节点
node.removeAttribute('v-model');
}
}
}
if(node.nodeType==3){
if(reg.test(node.nodeValue)){
var name = (node.nodeValue).match(reg)[1];
name = name.trim();
new Watcher(vm, node, name);//为节点添加订阅者方法
}
}
}
创建Vue构造方法:
function Vue(options){
this.data = options.data;
var data = this.data;
observe(data,this);
var id = options.el;
var dom = nodeToFragment(document.getElementById(id),this);
document.getElementById(id).appendChild(dom);
}
创建Vue示例:调用Vue,实现 输入框数据和data数据同步:
var vm = new Vue({
el: 'app',
data: {
name:'Lucy'
}
})