- Wather类: 观察数据变化并执行回调
- Compile类: 相当于解析DOM,处理插值表达式替换数据
- Dep类: 收集依赖,通知更新
- Observe类: 响应式数据,遍历对象key。并实现一个defineReactive方法定义响应式数据
发布订阅模式
class Watcher{
constructor(vm,key,callback){
this.vm = vm;
this.key = key;
this.callback = callback;
Dep.target = this;
this.oldValue = vm[key];
Dep.target = null;
}
update(){
const newValue = this.vm[this.key];
if(newValue === this.oldValue) return
this.callback(newValue);
this.oldValue = newValue;
}
}
class Compile{ //处理结构里的
constructor(vm){
this.vm = vm ;
this.el = vm.$el;
this.compile(this.el);
}
compile(el){ //有子节点
const childNodes = el.childNodes; //把所有节点类型弄进来
Array.from(childNodes).forEach(node=>{
//遍历判断
if(node.nodeType === 3){ //文本节点
this.compileText(node);
}else if(node.nodeType === 1){ //元素节点
}
if(node.childNodes && node.childNodes.length){ //如果这个节点还有子节点就再compile
this.compile(node);
}
})
}
compileText(node){ //处理文本类型节点
const reg = /\{\{(.+?)\}\}/g //匹配插值表达式
const value = node.textContent.replace(/\s/g,'');
const tokens = []; //保存要拆分的多个部分
let result,index,lastIndex = 0;
while(result = reg.exec(value)){
index = result.index;
if(index > lastIndex){
tokens.push(value.slice(lastIndex,index));
}
const key = result[1].trim();
tokens.push(this.vm[key]); //放入对应想要的值,比如{{mytite}},放进vm[mytitle]
lastIndex = index + result[0].length;
const pos = tokens.length - 1;
//更新操作
new Watcher(this.vm,key,newValue=>{
tokens[pos] = newValue;
node.textContent = tokens.join('');
})
}
if(lastIndex < value.length){
tokens.push(value.slice(lastIndex));
}
if(tokens.length){
node.textContent = tokens.join('');
}
}
}
class Dep{
constructor(){
this.subs = [];
}
addSub(sub){
this.subs.push(sub);
}
notify(){
this.subs.forEach(sub=> sub.update());
}
}
class Observer{ //响应式数据data是对象?
constructor(data){
this.data = data;
this.dep = new Dep()
this.walk(data); //遍历对象属性?
}
walk(data){
const dep = this.dep;
Object.keys(data).forEach(key=> defineReactive(data,key,data[key],dep));
}
}
function defineReactive(data,key,value,dep){
if(typeof value === 'obejct' && value!==null){
return new Observer(value);
}
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get(){
Dep.target && dep.addSub(Dep.target); //增加视图?
return value;
},
set(newValue){
if(value === newValue) return //如果值还是原来那个没必要更新
value = newValue;
if(typeof value === 'object' && value!==null){
return new Observer(value);
}
dep.notify(); //通知更新
}
})
}
class Vue{
constructor(options){
this.$options = options || {};
this.$data = options.data || {};
const el = options.el;
this.$el = typeof el === 'string' ? document.querySelector(el): del;
//1.将属性注入到Vue实例
proxy(this,this.$data); //就相当于把data里面的属性交给Vue代理,就相当于添加到了Vue实例的属性
//2.创建Observer进行 data 属性变化的观察
new Observer(this.$data);
//3.视图解析
new Compile(this);
}
}
function proxy(target,data){
Object.keys(data).forEach(key=>{
Object.defineProperty(target,key,{
enumerable:true,
configurable:true,
get(){
return data[key];
},
set(newValue){
data[key] = newValue;
}
})
})
}
实现:
<div id="app">
{{mytitle}} {{mytitle}}
<h2>{{nihao}}</h2>
</div>
<script src="./vue.js"></script>
<script>
let vue = new Vue({
el:'#app',
data:{
mytitle:"shabi",
nihao:"我是你好",
}
});
console.log(vue);
</script>