<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./Kvue.js" type="text/javascript"></script>
</head>
<body>
<div id="app">
{{ message }}fsdafdsa
<div>
<span>
<span>
<span> {{ message }}</span>
</span>
</span>
</div>
{{mydata}}
<div v-html="htmldata" ></div>
<input v-model="modelData" /> {{modelData}}
</div>
</body>
<script>
let vm = new Kvue({
el:"#app",
data:{
message:"测试数据1111",
mydata:"some value",
htmldata:"html数据",
modelData:"双绑数据"
}
})
</script>
class Kvue{
constructor(option){
this.option=option;
this._data=option.data;
this.observer();
this.compile();
}
//编译
compile(){
let ele=document.querySelector(this.option.el);
let childNodes=ele.childNodes;
this.compileNode(childNodes);
}
//对数据编译到元素上
compileNode(childNodes){
let textContent="";
let exp=null;
childNodes.forEach((node,index)=>{
if(node.nodeType==3){
textContent= node.textContent;
exp=/\{\{\s*(\S+)\s*\}\}/g;
if(exp.test(textContent)){
// 初次渲染;
let $1 = RegExp.$1;
node.textContent=this._data[$1];
new Watcher(this,$1,newValue=>{
node.textContent=newValue;
});
}
}
else if(node.nodeType==1){
let attrs= node.attributes;
[...attrs].forEach((attr,index)=>{
let name= attr.name;
let value= attr.value;
if(name=="v-model"){
node.addEventListener("input",ev=>{
this._data[value]=ev.target.value;
});
new Watcher(this,value,newValue=>{
node.value=newValue;
});
}
});
if(node.childNodes.length>0){
this.compileNode(node.childNodes);
}
}
});
}
observer(){
Object.keys(this._data).forEach((key)=>{
let dep=new Dep();
let value= this.option.data[key];
Object.defineProperty(this._data,key,{
configurable: true,
enumerable: true,
get(){
if(Dep.target){
dep.addSub(Dep.target);
}
return value;
},
set(newValue){
if(value!=newValue){
dep.notify(newValue);
value=newValue;
}
}
});
})
}
}
//发布订阅 Dep 类和 Watcher
class Dep{
constructor(){
this.subs=[];
}
addSub(sub) {//订阅事件
this.subs.push(sub);
}
notify(newValue) {//通知触发方法
this.subs.forEach(v => {
v.update(newValue);
})
}
}
class Watcher {
constructor(vm, exp, cb) {
Dep.target = this;
vm._data[exp];
this.cb = cb;
Dep.target = null
}
update(newValue) {
this.cb(newValue);
}
}