实现原理
画的一般将就看看
代码
class Vue{
constructor(options){
this.options = options;
this.$data = options.data;
this.$el = document.querySelector(options.el);
new Observer(this.$data);//监听data所有的属性
this._proxy();//代理,可以通过vue.name 访问data.name的值
new Compiler(this.$el, this);//解析模板
}
_proxy(){//代理把data的属性一个个添加到vue中
Object.keys(this.$data).forEach(key => {
Object.defineProperty(this, key, {
configurable:true,
enumerable:true,
get(){
return this.$data[key];
},
set(newVal){
this.$data[key] = newVal;
}
})
})
}
}
/**
*通过Object.defineProperty重写get、set方法实现监听
*get获取值时,dep添加Watcher对象
*set更新数据时,dep.notify通知所有订阅的Watcher对象执行update方法更新数据
*/
class Observer{//监听
constructor(data){
this.$data = data;
this.defineReactive();
}
defineReactive(){
Object.keys(this.$data).forEach(key => {
const dep = new Dep();//为属性创建Dep
let val = this.$data[key];
Object.defineProperty(this.$data, key, {
configurable:true,
enumerable:true,
get(){
if(Dep.target){//解析模板时创建Watcher实例,Dep.target指向这个实例
dep.addSubs(Dep.target);//添加订阅者
}
return val
},
set(newVal){
if(newVal === val){
return;
}
val = newVal;
dep.notify();//通知所有订阅者update数据
}
})
})
}
}
class Dep{
constructor(){
this.subs = [];
}
addSubs(sub){//添加订阅者
this.subs.push(sub);
}
notify(){//通知所有订阅者更新数据
this.subs.forEach(sub=>{
sub.update();
})
}
}
const reg = /\{\{(.*)\}\}/
class Compiler{//解析模板
constructor(el, vm){
this.$el = el;
this.$vm = vm;
this.frag = this.createDocumentFragment();//创建虚拟节点
this.$el.appendChild(this.frag);//虚拟节点挂载到页面
}
createDocumentFragment(){
const frag = document.createDocumentFragment();//创建虚拟节点
let child;
while(child = this.$el.firstChild){//获取第一个节点
this._compiler(child);//解析
//添加节点,appendChild会把this.$el.firstChild,移到创建的虚拟节点。
//所以可以while(child = this.$el.firstChild)一直遍历
frag.appendChild(child);
}
return frag;
}
_compiler(node){//只实现了简单的文本节点
if(node.nodeType === 3){//文本节点
if(reg.test(node.nodeValue)){//匹配到{{name}}
const name = RegExp.$1.trim();
new Watcher(node, name, this.$vm)//创建Watcher实例
}
}
}
}
class Watcher{
constructor(node, name, vm){
this.node = node;
this.name = name;
this.vm = vm;
Dep.target = this; //Dep.target指向Watcher本身(get时添加到dep中的数据来自于这)
this.update();//更新数据把data中的数据更新到模板:类似{{name}}=>张三
Dep.target = null;//更新数据时get方法把Watcher添加到dep中,此时完成了使命可以为null
}
update(){//把data中的数据添加到模板中
this.node.nodeValue = this.vm[this.name]
}
}