1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge" /> 7 <title>双向数据绑定demo</title> 8 </head> 9 <style> 10 input { 11 border: 1px solid #636336; 12 margin-right: 10px; 13 } 14 </style> 15 <body> 16 <div id="app"> 17 <input type="text" v-model="text" /> 18 {{ text }} 19 </div> 20 <script> 21 // 遍历data添加数据劫持 22 function observe(obj, vm) { 23 console.log(vm); 24 Object.keys(obj).forEach(function(key) { 25 defineReactive(vm, key, obj[key]); 26 }); 27 } 28 // 数据劫持 29 function defineReactive(obj, key, val) { 30 var dep = new Dep(); 31 Object.defineProperty(obj, key, { 32 get: function() { 33 // 添加订阅者 watcher 到主题对象 Dep 34 if (Dep.target) { 35 dep.addSub(Dep.target); 36 } 37 return val; 38 }, 39 set: function(newVal) { 40 if (newVal === val) return; 41 val = newVal; 42 // 作为发布者发出通知 43 dep.notify(); 44 } 45 }); 46 } 47 // 劫持dom节点 48 function nodeToFragment(node, vm) { 49 var nodes = document.createDocumentFragment(); 50 var child; 51 // appendChild 方法有个隐蔽的地方,就是调用以后 child 会从原来 DOM 中移除 52 // 所以,第二次循环时,node.firstChild 已经不再是之前的第一个子元素了 53 while (child = node.firstChild) { 54 compile(child, vm); 55 nodes.appendChild(child); // 将子节点劫持到文档片段中 56 } 57 /*let nodeList = node.childNodes; 58 for (var i = 0; i < nodeList.length; i++) { 59 compile(nodeList[i], vm); 60 nodes.appendChild(nodeList[i]); 61 }*/ 62 return nodes; 63 } 64 // 遍历节点,找出v-model和双花括号 65 function compile(node, vm) { 66 // 节点类型为元素 67 if (node.nodeType === 1) { 68 var attr = node.attributes; 69 // 解析属性 70 for (var i = 0; i < attr.length; i++) { 71 if (attr[i].nodeName == ‘v-model‘) { 72 var name = attr[i].nodeValue; // 获取 v-model 绑定的属性名 73 node.addEventListener(‘input‘, function(e) { 74 // 给相应的data属性赋值,进而触发该属性的set方法 75 vm[name] = e.target.value; 76 }); 77 node.value = vm[name]; // 将data的值赋给该node 78 node.removeAttribute(‘v-model‘); // 移除v-model属性 79 } 80 } 81 new Watcher(vm, node, name, ‘input‘); // 输入框节点 82 } 83 var reg = /\{\{(.*)\}\}/; // 匹配双花括号 84 // 节点类型为文本内容 85 if (node.nodeType === 3) { 86 if (reg.test(node.nodeValue)) { 87 var name = RegExp.$1; // 获取匹配到的字符串 88 name = name.trim(); 89 new Watcher(vm, node, name, ‘text‘); // 展示节点 90 } 91 } 92 } 93 class Watcher { 94 constructor (vm, node, name, nodeType) { 95 Dep.target = this; 96 this.name = name; 97 this.node = node; 98 this.vm = vm; 99 this.nodeType = nodeType; 100 this.update(); 101 Dep.target = null; 102 } 103 update () { 104 this.get(); 105 if (this.nodeType === ‘text‘) { 106 this.node.nodeValue = this.value; 107 } 108 if (this.nodeType === ‘input‘) { 109 this.node.value = this.value; 110 } 111 } 112 // 获取 data 中的属性值 113 get () { 114 this.value = this.vm[this.name]; // 触发相应属性的 get 115 } 116 } 117 /* 118 // 观察者对象 119 function Watcher(vm, node, name, nodeType) { 120 Dep.target = this; 121 this.name = name; 122 this.node = node; 123 this.vm = vm; 124 this.nodeType = nodeType; 125 this.update(); 126 Dep.target = null; 127 } 128 Watcher.prototype = { 129 update: function() { 130 this.get(); 131 if (this.nodeType === ‘text‘) { 132 this.node.nodeValue = this.value; 133 } 134 if (this.nodeType === ‘input‘) { 135 this.node.value = this.value; 136 } 137 }, 138 // 获取 data 中的属性值 139 get: function() { 140 this.value = this.vm[this.name]; // 触发相应属性的 get 141 } 142 }; 143 */ 144 class Dep { 145 constructor () { 146 this.subs = []; 147 } 148 addSub (sub) { 149 this.subs.push(sub); 150 } 151 notify () { 152 this.subs.forEach(function(sub) { 153 sub.update(); 154 }); 155 } 156 } 157 /* 158 function Dep() { 159 this.subs = []; 160 } 161 Dep.prototype = { 162 addSub: function(sub) { 163 this.subs.push(sub); 164 }, 165 notify: function() { 166 this.subs.forEach(function(sub) { 167 sub.update(); 168 }); 169 } 170 }; 171 */ 172 // 创建Vue 173 function Vue(options) { 174 this.data = options.data; 175 var data = this.data; 176 observe(data, this); 177 var id = options.el; 178 var dom = nodeToFragment(document.getElementById(id), this); 179 // 编译完成后,将 dom 返回到 app 中 180 document.getElementById(id).appendChild(dom); 181 } 182 // 实例化Vue 183 var vm = new Vue({ 184 el: ‘app‘, 185 data: { 186 text: ‘hello world‘ 187 } 188 }); 189 </script> 190 </body> 191 </html>
原文地址:https://www.cnblogs.com/xinsir/p/12161377.html
时间: 2024-11-10 12:26:04