转:mvvm原理
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Two-way-data-binding</title> 6 </head> 7 <body> 8 9 <div id="app"> 10 <input type="text" v-model="text"> 11 {{ text }} 12 </div> 13 14 <script> 15 function observe (obj, vm) { 16 Object.keys(obj).forEach(function (key) { 17 defineReactive(vm, key, obj[key]); 18 }) 19 } 20 function defineReactive (obj, key, val) { 21 var dep = new Dep(); 22 Object.defineProperty(obj, key, { 23 get: function () { 24 // 添加订阅者 watcher 到主题对象 Dep 25 if (Dep.target) dep.addSub(Dep.target); 26 return val 27 }, 28 set: function (newVal) { 29 if (newVal === val) return 30 val = newVal; 31 // 作为发布者发出通知 32 dep.notify(); 33 } 34 }); 35 } 36 function nodeToFragment (node, vm) { 37 var flag = document.createDocumentFragment(); 38 var child; 39 // 许多同学反应看不懂这一段,这里有必要解释一下 40 // 首先,所有表达式必然会返回一个值,赋值表达式亦不例外 41 // 理解了上面这一点,就能理解 while (child = node.firstChild) 这种用法 42 // 其次,appendChild 方法有个隐蔽的地方,就是调用以后 child 会从原来 DOM 中移除 43 // 所以,第二次循环时,node.firstChild 已经不再是之前的第一个子元素了 44 while (child = node.firstChild) { 45 compile(child, vm); 46 flag.appendChild(child); // 将子节点劫持到文档片段中 47 } 48 return flag 49 } 50 function compile (node, vm) { 51 var reg = /\{\{(.*)\}\}/; 52 // 节点类型为元素 53 if (node.nodeType === 1) { 54 var attr = node.attributes; 55 // 解析属性 56 for (var i = 0; i < attr.length; i++) { 57 if (attr[i].nodeName == 'v-model') { 58 var name = attr[i].nodeValue; // 获取 v-model 绑定的属性名 59 node.addEventListener('input', function (e) { 60 // 给相应的 data 属性赋值,进而触发该属性的 set 方法 61 vm[name] = e.target.value; 62 }); 63 node.value = vm[name]; // 将 data 的值赋给该 node 64 node.removeAttribute('v-model'); 65 } 66 }; 67 new Watcher(vm, node, name, 'input'); 68 } 69 // 节点类型为 text 70 if (node.nodeType === 3) { 71 if (reg.test(node.nodeValue)) { 72 var name = RegExp.$1; // 获取匹配到的字符串 73 name = name.trim(); 74 new Watcher(vm, node, name, 'text'); 75 } 76 } 77 } 78 function Watcher (vm, node, name, nodeType) { 79 Dep.target = this; 80 this.name = name; 81 this.node = node; 82 this.vm = vm; 83 this.nodeType = nodeType; 84 this.update(); 85 Dep.target = null; 86 } 87 Watcher.prototype = { 88 update: function () { 89 this.get(); 90 if (this.nodeType == 'text') { 91 this.node.nodeValue = this.value; 92 } 93 if (this.nodeType == 'input') { 94 this.node.value = this.value; 95 } 96 }, 97 // 获取 data 中的属性值 98 get: function () { 99 this.value = this.vm[this.name]; // 触发相应属性的 get 100 } 101 } 102 function Dep () { 103 this.subs = [] 104 } 105 Dep.prototype = { 106 addSub: function(sub) { 107 this.subs.push(sub); 108 }, 109 notify: function() { 110 this.subs.forEach(function(sub) { 111 sub.update(); 112 }); 113 } 114 } 115 function Vue (options) { 116 this.data = options.data; 117 var data = this.data; 118 observe(data, this); 119 var id = options.el; 120 var dom = nodeToFragment(document.getElementById(id), this); 121 // 编译完成后,将 dom 返回到 app 中 122 document.getElementById(id).appendChild(dom); 123 } 124 var vm = new Vue({ 125 el: 'app', 126 data: { 127 text: 'hello world' 128 } 129 }) 130 </script> 131 </body> 132 </html>
自己也写了一份,但是,dom对象到view这一层并没有实现
浙公网安备 33010602011771号