简易的MVVM双向数据绑定原理
1 //数据源 2 let data = { 3 message: "hello word", 4 info: { 5 job: "web It", 6 level: "p8" 7 } 8 }; 9 //对数据源劫持拦截 10 class Observer { 11 constructor(data) { 12 //需要观察劫持的对象 13 this.data = data; 14 //给对象设置get,set 15 this.observer(this.data); 16 } 17 observer(data) { 18 //对数据源所有的属性添加get,set 19 Object.keys(data).forEach(key => { 20 this.defineReactive(data, key, data[key]); 21 //如果data[key]还是一个对象 22 if (data[key] instanceof Object) { 23 this.observer(data[key]); 24 } 25 }); 26 } 27 defineReactive(data, key, value) { 28 let dep = new Dep(); 29 Object.defineProperty(data, key, { 30 enumerable: true, 31 configurable: true, 32 get() { 33 Dep.target && dep.add(Dep.target); 34 return value; 35 }, 36 set(newValue) { 37 //如果新旧值变化 38 if (value !== newValue) { 39 value = newValue; 40 dep.notify(); 41 } 42 } 43 }); 44 } 45 } 46 //定义一个监听watcher 47 class Watcher { 48 constructor(data, key, callback) { 49 //保存数据源 50 this.data = data; 51 //保存取值key 52 this.key = key; 53 //保存回调 54 this.callback = callback; 55 //保存老值 56 this.oldValue = this.get(this.data, this.key); 57 } 58 getExpressionValue(data, expr) { 59 let expression = expr.split("."), 60 [length] = expression; 61 return expression.reduce((prev, next, index) => { 62 if (index === length - 1) { 63 return prev[expression[1]]; 64 } 65 return prev[next]; 66 }, data); 67 } 68 //触发获取值的操作 69 get(data, key) { 70 //当每一次去数据源获取值的时候把watcher自身赋值给Dep订阅类上 71 Dep.target = this; 72 let val = this.getExpressionValue(data, key); 73 Dep.target = null; 74 return val; 75 } 76 update() { 77 //取出设置完的值就行新旧比对 78 let newValue = this.getExpressionValue(this.data, this.key); 79 newValue !== this.oldValue && this.callback(newValue); 80 } 81 } 82 //创建订阅发布 83 class Dep { 84 constructor() { 85 //订阅者列表 86 this.subscribeLists = new Set(); 87 } 88 add(watcher) { 89 //添加订阅 90 this.subscribeLists.add(watcher); 91 } 92 notify() { 93 this.subscribeLists.forEach(watcher => watcher.update()); 94 } 95 } 96 97 function getExpressionValue(data, expr) { 98 let expression = expr.split("."), 99 [length] = expression; 100 return expression.reduce((prev, next, index) => { 101 if (index === length - 1) { 102 return prev[expression[1]]; 103 } 104 return prev[next]; 105 }, data); 106 } 107 108 //取值 109 function getVal(key) { 110 new Watcher(data, key, newValue => { 111 text.innerHTML = newValue; 112 }); 113 return getExpressionValue(data, key); 114 } 115 //对数据源进行劫持 116 const observer = new Observer(data); 117 118 let text = document.querySelector("#text"); 119 text.innerHTML = getVal("info.level"); 120 document.querySelector("#input").addEventListener("input", e => { 121 data.info.level = e.target.value 122 });