index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue响应话实现数据双向绑定</title>
</head>
<body>
<div id="app">
<p></p>
<p></p>
<p v-text="name"></p>
</div>
<script src="compile.js"></script>
<script src="cvue.js"></script>
<script>
const app=new CVue({
el:'#app',
data:{
name:'i am cxh',
age:18,
foo:'foo',
bar:{
mua:'mua'
}
},
created(){
console.log('改变数据')
setTimeout(() => {
this.name='我是测试'
this.age=28
}, 1000);
}
})
// app.name="cxh";
// app.age=20;
// console.log(app.$data);
// const app=new CVue({
// el:'#app',
// data:{
// name:'cxh',
// age:18,
// foo:'foo',
// bar:{mua:'mua'}
// }
// })
</script>
</body>
</html>
cvue.js(vue 对象,weatcher,dep对象)
class CVue{
constructor(options){
this.$options=options;
this.$data=options.data;
this.observe(this.$data);
// console.log(this);
// new Watcher(this,'foo');
// this.foo;
// this.foo='123';
// new Watcher(this,'name');
// this.name;
// this.name='hhh';
// new Watcher(this,'bar');
// this.bar='null';
new Compile(options.el,this);
if(options.created){
options.created.call(this)
}
}
observe(data){
if(!data||typeof data!='object'){
return;
}
Object.keys(data).forEach(key=>{
this.defineReactive(data,key,data[key]);
this.proxyData(key)
})
}
defineReactive(obj,key,value){
this.observe(value);
const dep=new Dep()
Object.defineProperty(obj,key,{
get(){
//依赖收集
Dep.target && dep.addDep(Dep.target)
// console.log(Dep.target);
// console.log(dep.watchers);
return value;
},
set(newValue){
if(newValue!=value){
value=newValue;
// console.log(key +'更新了');
dep.notify();
}
}
})
}
proxyData(key){
Object.defineProperty(this,key,{
get(){
return this.$data[key];
},
set(newValue){
this.$data[key]=newValue;
}
})
}
}
class Dep{
constructor(){
this.watchers=[];
}
addDep(watcher){
this.watchers.push(watcher);
}
notify(){
this.watchers.forEach(watcher=>{
watcher.update();
})
}
}
class Watcher{
constructor(vm,key,cb){
this.$vm=vm;
this.$key=key;
this.cb=cb;
Dep.target=this;
this.$vm[this.$key];
Dep.target=null;
}
update(){
this.cb.call(this.$vm,this.$vm[this.$key])
console.log(this.$key+'更新了');
}
}
compile.js(编译)
// 遍历dom,解析指令和插值表达式
class Compile{
// el-编译模板,vm-KVue实列
constructor(el,vm){
this.$vm=vm;
this.$el=document.querySelector(el)
//将模板内容移到片段
this.$fragment=this.node2fragment(this.$el);
//执行编译
this.compile(this.$fragment);
//解析完放回$el中
this.$el.appendChild(this.$fragment);
}
node2fragment(el){
//创建文档片段
const fragment=document.createDocumentFragment();
let child;
while(child = el.firstChild){
fragment.appendChild(child)
}
return fragment;
}
compile(el){
const childNodes=el.childNodes;
Array.from(childNodes).forEach(node =>{
if(node.nodeType==1){
//
console.log('编译元素'+node.nodeName);
}else if(this.isInter(node)){
//
console.log('编译文本'+node.textContent);
this.compileText(node)
}
//递归编译
if(node.children&&node.childNodes.length>0){
this.compile(node)
}
})
}
isInter(node){
return node.nodeType==3 &&/\{\{(.*)\}\}/.test(node.textContent);
}
// 文本替换
compileText(node){
const exp=RegExp.$1;
this.update(node,exp,'text')
// node.textContent=this.$vm[RegExp.$1]
}
update(node,exp,dir){
const updater =this[dir+'Updater'];
updater&&updater(node,this.$vm[exp]);
//创建watcher实列完成依赖收集
new Watcher(this.$vm,exp,function(value){
updater&& updater(node,value);
})
}
textUpdater(node,value){
node.textContent=value;
}
}