<!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