<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport" content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><divid="app"><h2>{{title}}</h2><h1>{{name}}</h1><inputtype="text" v-model="name" v-on:blur="blur"><buttonv-on:click="clickMe">click me!</button></div><script>// 监听器functionobserve(data){if(Object.prototype.toString.call(data)!=='[object Object]'){return;}Object.keys(data).forEach(key=>{defineReactive(data,key,data[key])})}functiondefineReactive(data,key,value){observe(value);vardep=newDep();Object.defineProperty(data,key,{enumerable: true,configurable: true,get: function(){if(Dep.target){dep.addSub(Dep.target);// 添加一个订阅者}returnvalue;},set: function(newVal){if(newVal===value){return;}console.log('value changed,\nthe old value is '+value+',\nthe new value is '+newVal);value=newVal;dep.notify();// 如果数据变化,通知所有订阅者}})}// 消息订阅器functionDep(){this.subs=[];}Dep.prototype={constructor: Dep,addSub: function(sub){this.subs.push(sub);},notify: function(){this.subs.map(sub=>{sub.update();})}}// 订阅器functionWatcher(vm,exp,cb){this.vm=vm;this.exp=exp;this.cb=cb;this.value=this.get();}Watcher.prototype={constructor: Watcher,get: function(){Dep.target=this;varvalue=this.vm.data[this.exp]// 强制执行监听器里的get函数Dep.target=null;returnvalue;},update: function(){this.run();},run: function(){varvalue=this.vm.data[this.exp];varoldVal=this.value;if(value!==oldVal){this.value=value;this.cb.call(this.vm,value,oldVal);}}}// 解析器/* 将需要解析的dom节点存入fragment片段 */functionCompile(el,vm){this.vm=vm;console.log(this.vm);this.el=document.querySelector(el);this.fragment=null;this.init();}Compile.prototype={constructor: Compile,init: function(){if(!this.el){return;}this.fragment=this.nodeToFragment(this.el);this.compileElement(this.fragment);this.el.appendChild(this.fragment);},nodeToFragment: function(el){varfragment=document.createDocumentFragment();varchild=el.firstChild;while(child){fragment.appendChild(child);child=el.firstChild;}returnfragment;},compileElement: function(el){varchildNodes=el.childNodes;varself=this;[].slice.call(childNodes).forEach(child=>{vartext=child.textContent;varreg=/\{\{(.*)\}\}/;if(self.isElementNode(child)){self.compileDirective(child);}if(self.isTextNode(child)&®.test(text)){self.compileText(child,reg.exec(text)[1]);}if(child.childNodes&&child.childNodes.length){self.compileElement(child);}})},compileDirective: function(node){varattrs=node.attributes;varself=this;[].slice.call(attrs).forEach(attr=>{varname=attr.name;if(self.isDirective(name)){varexp=attr.value;vardir=name.substring(2);if(self.isEventDirentive(dir)){// 事件指令console.log('event')self.compileEvent(node,self.vm,exp,dir)}else{// v-model指令self.compileModel(node,self.vm,exp,dir)}node.removeAttribute(name);}})},compileText: function(node,exp){varself=this;varinitText=this.vm[exp];this.updateText(node,initText);newWatcher(this.vm,exp,function(value){self.updateText(node,value)})},compileEvent: function(node,vm,exp,dir){vareventName=dir.split(':')[1];varcb=this.vm.methods&&this.vm.methods[exp];if(eventName&&cb){console.log('bind event')node.addEventListener(eventName,cb.bind(vm),false);}},compileModel: function(node,vm,exp,dir){varself=this;varval=this.vm[exp];this.modelUpdater(node,val);newWatcher(this.vm,exp,function(value){self.modelUpdater(node,value);})node.addEventListener('input',function(e){varnewValue=e.target.value;if(newValue===val){return}self.vm[exp]=newValue;val=newValue;})},updateText: function(node,text){node.textContent=typeoftext==='undefined' ? '' : text;},modelUpdater: function(node,text){node.value=typeoftext==='undefined' ? '' : text;},isTextNode: function(node){returnnode.nodeType===3;},isElementNode: function(node){returnnode.nodeType===1;},isDirective: function(directive){returndirective.indexOf('v-')===0;},isEventDirentive: function(directive){returndirective.indexOf('on:')===0;}}functionSelfVue(options){varself=this;this.data=options.data;this.methods=options.methods;Object.keys(this.data).forEach(key=>{self.proxyKey(key);})observe(this.data);newCompile(options.el,this);options.mounted.call(this);}SelfVue.prototype.proxyKey=function(key){varself=this;Object.defineProperty(self,key,{get: function(){returnself.data[key];},set: function(newVal){self.data[key]=newVal;}})}varselfVue=newSelfVue({el: '#app',data: {title: 'hello world',name: 'canfoo'},methods: {clickMe: function(){this.title='hello world';}},mounted: function(){window.setTimeout(()=>{this.title='你好';},1000);}})</script></body></html>