/**
*
* 基本原理:
* 1、通过Observer劫持data上的对象并监听data上的所有属性,遍历所有属性,并用Object.defineProperty转化为getter/setter,监听data上属性的的变化
* 2、将data上的属性挂载到vue的实例上,实例化后可以在vue使用this访问data属性
* 3、使用Compiler解析模板
* 4、Watcher:添加订阅者,访问和修改时update更新视图
* 5、Depend:addSub管理订阅数据对象,数据变化时notify通知所有数据对象进行视图更新
*/
function myVue(options={}){
this.$options = options;
this.$data = typeof this.$options == 'function' ? this.options.data() : this.$options.data;
this.$el = this.$options.el;
// 将this.$data添加到响应式系统
new Observer(this.$data)
//将data上的属性添加到vue中
this.$proxy();
//解析模板
new Compiler(this.$el,this.$data);
this.$options.created.call(this);
}
myVue.prototype.$proxy = function () {
Object.keys(this.$data).forEach(key=>{
Object.defineProperty(this,key,{
configurable:true,
enumerable:true,
set(val){
this.$data[key] = val;
},
get(){
return this.$data[key]
}
});
})
}
// 响应式系统
function Observer($data){
Object.keys($data).forEach(key=>{
this.$ref($data,key,$data[key])
})
}
Observer.prototype.$ref = function(_data,key,value){
let dep = new Depend();
Object.defineProperty(_data,key,{
configurable:true,
enumerable:true,
set(val){
if(value===val) return;
value = val;
dep.notify();
},
get(){
Depend.target && dep.addSub(Depend.target)
return value
}
})
}
// 依赖
function Depend(){
// 订阅者数组
this.subs = []
}
// 添加订阅者
Depend.prototype.addSub = function(sub){
this.subs.push(sub)
}
Depend.prototype.notify = function(){
this.subs.forEach(sub=>{
sub.update();
})
}
// 订阅者
function Watcher(node,name,data){
this.node = node;
this.name = name;
this.data = data;
Depend.target = this;
this.update();
Depend.target = null;
}
Watcher.prototype.update = function(){
if(this.node.nodeType===1){
this.node.value = this.data[this.name]
} else if (this.node.nodeType === 3){
this.node.nodeValue = this.data[this.name]
}
}
let reg = /\{\{(.+)\}\}/;
// 解析模板
function Compiler(el,data){
this.el = document.querySelector(el);
this.data = data;
let frag = this.createFragment();
this.el.appendChild(frag);
}
Compiler.prototype.createFragment = function(){
let child;
let frag = document.createDocumentFragment();
while(child = this.el.firstChild){
this._compiler(child);
frag.appendChild(child);
}
return frag
}
Compiler.prototype._compiler = function(node){
// 标签节点
if(node.nodeType===1){
node.childNodes.forEach(item=>{
this._compiler(item);
})
// 文本节点
}else if(node.nodeType===3){
if(reg.test(node.nodeValue)){
const name = reg.exec(node.nodeValue)[1]
new Watcher(node,name,this.data)
}
}
}
myVue