<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>vue双向绑定原理分析</title></head><style> body{ margin: 0; padding: 0; }</style><body><div id="app"> {{name}}</div></body><script> //vue类 class Vue { constructor(options={}) { this.data = options.data this.el = options.el // 数据劫持 observe(this.data) // 模版编译 compile(this.el || document.body,this) } } class Ovserver { constructor(data) { this.data = data this.walk(this.data) } walk(data) { Object.keys(data).forEach(key=>objectDefinedDate(data,key,data[key])) } } observe=(data)=>{ if (!data || typeof data !=='object') { return } return new Ovserver(data) //Object.keys(data).forEach(key=>objectDefinedDate(data,key,data[key])) } objectDefinedDate = (data,key,val)=> { let dep = new Dep() Object.defineProperty(data,key,{ enumberable:true, configurable:true, get:()=>{ // 添加订阅者 if (Dep.target) { dep.addSub(Dep.target) // Dep.target为一个全局的对象,是Watcher的实例,拥有Watcher的一切属性和方法 } return val }, set:(newVal)=>{ if (newVal === val) { return } val = newVal // 通知更新 dep.notify() } }) } class Dep { constructor() { this.subs = [] // 存放订阅者 订阅者就是观察者,即观察者实例 } addSub(sub){ this.subs.push(sub) } notify() { this.subs.forEach(sub=>sub.update()) // 调用Watcher的实例的更新方法 } } // 模版编译 compile =(node,vm)=> { let reg = /\{\{(.*)\}\}/g // 节点类型为元素 if (node.nodeType === 1) { if (reg.test(node.innerHTML)){ let name = RegExp.$1 node.textContent = vm.data[name] // 添加观察者 new Watcher(vm,name,function (newVal) { node.textContent = newVal }) } } } class Watcher { constructor(vm,node,fn) { this.vm = vm // vue实例 this.node = node // 绑定的节点 this.fn = fn // 更新视图的回调函数 this.value = this.get() // 触发 Object.defineProperty的get方法,而后触发添加订阅者方法 } update(){ let value = this.vm.data[this.node] let oldVal = this.value if (value !== oldVal) { this.value = value this.fn.call(this.vm,value,oldVal) } } get(){ Dep.target = this // this表示当前的Watcher的实例 let value = this.vm.data[this.node] // 触发 Object.defineProperty的get方法 Dep.target = null return value } } let el = document.getElementById('app') const vue = new Vue({ data:{ name:'vue' }, el:el }) setTimeout(()=>{ vue.data.name='vue change' },2000)</script></html>参考https://juejin.im/post/5acd0c8a6fb9a028da7cdfaf