vue.js 中双向绑定的实现---初级

1. 1 我们看到的变量,其实都不是独立的,它们都是windows对象上的属性

 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>Document</title>
 8 </head>
 9 <body>
10     <script>
11         var a = 117;
12         console.log(a);
13         console.log(window.a);
14     </script>
15 </body>
16 </html>

运行结果:直接打印a 和 打印window下的a 结果一样

1.2  defineProperty():  每个对象在对本身属性的设置或者调用时,都会调用set()或则get()函数;这个和php类的调用差不多,在php中,叫做魔术方法,在调用类时,会调用__construct()方法,在结束调用时,会调用__destruct() 方法;

 1  <script>
 2         var obj = {};
 3         Object.defineProperty(obj, 'name1', {
 4             set: function() {
 5                 console.log("set()方法被调用了");
 6             },
 7             get: function() {
 8                 console.log("get()方法被调用了");
 9             }
10         });
11         var a = obj.name1; // 调用get()方法
12         obj.name1 = "huanying2015"; // 调用set()方法
13 </script>

运行结果:

2.  对obj对象的属性进行监控,当属性改变时,调用set方法:其实这里监控的也不是obj的属性,而是监控输入框value值的变换,通过keyup 事件来监控

 1 <head>
 2     <meta charset="UTF-8">
 3     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 4     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 5     <title>Document</title>
 6     <script>
 7         window.onload = function() {
 8             var OInput = document.querySelector("#aa");
 9             var Odiv = document.querySelector("#bb");
10             var obj = {};
11             Object.defineProperty(obj, 'content', {
12                 set: function(val) {
13                     OInput.value = val;
14                     Odiv.innerHTML = val;
15                 },
16             });
17             // e.target.value获取的就是你选择接受事件的元素输入的或者选择的值。
18             // 参数e接收事件对象。
19             // 而事件对象也有很多属性和方法, 其中target属性是获取触发事件对象的目标, 也就是绑定事件的元素, 
20             // e.target表示该DOM元素, 然后在获取其相应的属性值。
21             OInput.addEventListener('keyup', function(e) {
22                 obj.content = e.target.value;
23             });
24         }
25     </script>
26 </head>
27 <body>
28     <input type="text" id="aa">
29     <div id="bb"></div>
30 </body>

运行结果:

3. 双向绑定预热之------碎片节点说明:创建一个碎片节点---->然后在id 为box的范围内查找节点,把所有的节点都插入到碎片节点中,然后在把碎片节点插入到id为box 的范围内,即先把 id = box 中的东西拿出来,然后玩一玩,检查一下,再把东西放回去   

注意:DocumentFragment节点不属于文档树,继承的parentNode属性总是null。它有一个很实用的特点,当请求把一个DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而是它的所有子孙节点。

 1 <body>
 2     <div id="box">
 3         <input type="text" id="txt">
 4         <span id="box2"></span>
 5     </div>
 6     <script>
 7         function nodeToFragment(node) {
 8             var frag = document.createDocumentFragment();
 9             var childNode;
10             // 这里childNode 和node.firstChild 都同时指向node 的第一个节点,放在while 中,是作为一个判断语句使用,
11             // 如果node.firstChild 为true ,即有第一个节点,那么执行do的内容(while(true){do;}),如果没有第一个节点,那么终止循环
12             while (childNode = node.firstChild) {
13                 // 1.把node的第一个子节点插入碎片节点中
14                 // 2.注意注意:
15                 // 3.有一个很重要的特性是,如果使用appendChid方法将原dom树中的节点添加到DocumentFragment中时,会删除原来的节点。 
16                 // 4.以上的第3点是while循环的基础:所以每次循环,node.firstChild 都指向不同的节点,直到循环完毕
17                 frag.appendChild(childNode);
18             }
19             return frag;
20         }
21         var oBox = document.querySelector("#box");
22         var nodeList = nodeToFragment(oBox);
23         // 把原先被删除的节点重新一次性插入原处,当插入的节点比较多时,这可以调高效率,减少页面对DOM的渲染次数
24         oBox.appendChild(nodeList);
25     </script>
26 </body>

运行结果:

4. 绑定编译原理:

 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>Document</title>
 8     <script>
 9         window.onload = function() {
10             // 替换函数:对html 中的content 进行替换
11             function compile(node, ghost) {
12                 // 当需要替换的节点为元素节点时(即节点类型 nodeType ==1),遍历元素中的属性节点,查找元素中有没有'v-model'属性,如果有,则进行替换
13                 if (node.nodeType == 1) {
14                     // 获取元素的所有属性,使用attributes属性获取
15                     var attrs = node.attributes;
16                     for (var i = 0, len = attrs.length; i < len; i++) {
17                         // 当元素的属性名字为‘v-model’时
18                         if (attrs[i].nodeName == "v-model") {
19                             // 获取'v-model'属性的属性值,存在key中
20                             var key = attrs[i].nodeValue;
21                             // 设置元素节点的value 属性为 ghost.data.key  // 所有的.都可以使用[]来替代//
22                             node.value = ghost.data[key];
23                             // 清除元素中的 ‘v-mdel’属性
24                             node.removeAttribute('v-model');
25                         }
26                     }
27                 };
28                 // 设置正则匹配
29                 var re = /{{(.*)?}}/;
30                 // 如果为文本节点,即属性值为3时,
31                 if (node.nodeType == 3) {
32                     // 查找文本节点的值,如果匹配正则
33                     if (re.test(node.nodeValue)) {
34                         // 获取正则中的第一个匹配对象,即{{}}中的内容
35                         var key = RegExp.$1;
36                         // 清除两侧的空格
37                         key = key.trim();
38                         // 将文本内容替换为 ghost.data[key];
39                         node.nodeValue = ghost.data[key];
40                     }
41                 }
42             };
43             // 替换函数
44             function nodeToFragment(node, ghost) {
45                 var flag = document.createDocumentFragment();
46                 var childNode;
47                 while (childNode = node.firstChild) {
48                     compile(childNode, ghost);
49                     flag.appendChild(childNode);
50                 }
51                 return flag;
52             };
53             // 传值函数
54             function Ghost(opt) {
55                 this.data = opt.data;
56                 var id = opt.el;
57                 var Obox = document.querySelector(id);
58                 var node = nodeToFragment(Obox, this);
59                 Obox.appendChild(node);
60             }
61             // 实例化函数,这里相当于在vue.js 中的 new Vue({});
62             new Ghost({
63                 el: "#box",
64                 data: {
65                     content: 'hello,huanying2015,how are you?'
66                 }
67             });
68         }
69     </script>
70 </head>
71 <body>
72     <div id="box">
73         <input type="text" id="txt" v-model="content">
74         <br> {{content}}
75     </div>
76 </body>
77 </html>

运行结果:

5.相应式绑定数据:

  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>Document</title>
  8     <script>
  9         window.onload = function() {
 10             function responsive(obj, key, val) {
 11                 Object.defineProperty(obj, key, {
 12                     get: function() {
 13                         return val;
 14                     },
 15                     set: function(newVal) {
 16                         if (val == newVal) {
 17                             return;
 18                         };
 19                         val = newVal;
 20                         console.log('值改变了,新值为:' + val);
 21                     }
 22                 });
 23             };
 24 
 25             function observe(obj, ghost) {
 26                 Object.keys(obj).forEach(function(key) {
 27                     responsive(ghost, key, obj[key]);
 28                 });
 29             };
 30             // 替换函数:对html 中的content 进行替换
 31             function compile(node, ghost) {
 32                 // 当需要替换的节点为元素节点时(即节点类型 nodeType ==1),遍历元素中的属性节点,查找元素中有没有'v-model'属性,如果有,则进行替换
 33                 if (node.nodeType == 1) {
 34                     // 获取元素的所有属性,使用attributes属性获取
 35                     var attrs = node.attributes;
 36                     for (var i = 0, len = attrs.length; i < len; i++) {
 37                         // 当元素的属性名字为‘v-model’时
 38                         if (attrs[i].nodeName == "v-model") {
 39                             var key = attrs[i].nodeValue;
 40                             // 这里 ghost[key] 只是一个中间变量 
 41                             // 监控键盘松开事件,当键盘松开时,在ghost上增加一个属性key,然后获取监听目标的值,把它赋值给ghost[key]
 42                             node.addEventListener('keyup', function(ev) {
 43                                 ghost[key] = ev.target.value;
 44                             });
 45                             // 然后把这个ghost[key]赋值给节点的value 属性
 46                             node.value = ghost[key];
 47                             // 删除属性节点‘v-model’
 48                             node.removeAttribute('v-model');
 49                         }
 50                     }
 51                 };
 52                 // 设置正则匹配
 53                 var re = /{{(.*)?}}/;
 54                 // 如果为文本节点,即属性值为3时,
 55                 if (node.nodeType == 3) {
 56                     // 查找文本节点的值,如果匹配正则
 57                     if (re.test(node.nodeValue)) {
 58                         // 获取正则中的第一个匹配对象,即{{}}中的内容
 59                         var key = RegExp.$1;
 60                         // 清除两侧的空格
 61                         key = key.trim();
 62                         // 将文本内容替换为 ghost.data[key];
 63                         node.nodeValue = ghost.data[key];
 64                     }
 65                 }
 66             };
 67             // 替换函数
 68             function nodeToFragment(node, ghost) {
 69                 var flag = document.createDocumentFragment();
 70                 var childNode;
 71                 while (childNode = node.firstChild) {
 72                     compile(childNode, ghost);
 73                     flag.appendChild(childNode);
 74                 }
 75                 return flag;
 76             };
 77             // 传值函数
 78             function Ghost(opt) {
 79                 this.data = opt.data;
 80                 var data = this.data;
 81                 var id = opt.el;
 82                 // 对当前对象及当前对象的data 数据对象进行监控
 83                 observe(data, this);
 84                 var Obox = document.querySelector(id);
 85                 var node = nodeToFragment(Obox, this);
 86                 Obox.appendChild(node);
 87             }
 88             // 实例化函数,这里相当于在vue.js 中的 new Vue({});
 89             new Ghost({
 90                 el: "#box",
 91                 data: {
 92                     content: 'hello,huanying2015,how are you?'
 93                 }
 94             });
 95         }
 96     </script>
 97 </head>
 98 <body>
 99     <div id="box">
100         <input type="text" id="txt" v-model="content">
101         <br> {{content}}
102     </div>
103 </body>
104 </html>

运行结果:

 

6. 发布者,订阅者模式解析:

 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>Document</title>
 8     <script>
 9         // 发布者1 发布信息
10         sub1 = {
11                 update: function() {
12                     console.log("这是发布者1发出的信息");
13                 }
14             }
15             // 发布者2 发布信息
16         sub2 = {
17                 update: function() {
18                     console.log("这是发布者2发出的信息");
19                 }
20             }
21             // 发布者3 发布信息
22         sub3 = {
23                 update: function() {
24                     console.log("这是发布者3发出的信息");
25                 }
26             }
27             //发布清单构造函数
28         function Dep() {
29             this.subs = [sub1, sub2, sub3];
30         }
31         // 发布清单原型对象上加nodify()方法
32         Dep.prototype.nodify = function() {
33                 this.subs.forEach(function(sub) {
34                     // 执行发布框架操作
35                     sub.update();
36                 });
37             }
38             // 实例化发布清单,即填写发布清单
39         var dep = new Dep();
40         // 一个实体指定出一套自己的发布方法
41         var pub = {
42                 publish: function() {
43                     // 发布方法中,执行发布清单的操作
44                     dep.nodify();
45                 }
46             }
47             // 发布执行
48         pub.publish();
49     </script>
50 </head>
51 <body>
52 </body>
53 </html>

运行结果:

7. 响应式数据绑定:即输入的同时,显示输出

  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>Document</title>
  8     <script>
  9         window.onload = function() {
 10             var Watcher = function(ghost, node, name) {
 11                 Dep.target = this;
 12                 this.name = name;
 13                 this.node = node;
 14                 this.ghost = ghost;
 15                 this.update();
 16                 Dep.target = null;
 17             }
 18             Watcher.prototype = {
 19                 update: function() {
 20                     this.get();
 21                     this.node.nodeValue = this.value;
 22                 },
 23                 get: function() {
 24                     this.value = this.ghost[this.name];
 25                 }
 26             }
 27 
 28             function Dep() {
 29                 this.subs = [];
 30             }
 31             Dep.prototype = {
 32                 addsub: function(sub) {
 33                     this.subs.push(sub);
 34                 },
 35                 nodify: function() {
 36                     this.subs.forEach(function(sub) {
 37                         sub.update();
 38                     });
 39                 }
 40             }
 41 
 42             function responsive(obj, key, val) {
 43                 var dep = new Dep();
 44                 Object.defineProperty(obj, key, {
 45                     get: function() {
 46                         if (Dep.target) {
 47                             dep.addsub(Dep.target);
 48                         }
 49                         console.log("access:" + val);
 50                         return val;
 51                     },
 52                     set: function(newVal) {
 53                         if (val == newVal) {
 54                             return;
 55                         }
 56                         val = newVal;
 57                         console.log("值改变了:" + val);
 58                         dep.nodify();
 59                     }
 60                 });
 61             }
 62 
 63             function observe(obj, ghost) {
 64                 Object.keys(obj).forEach(function(key) {
 65                     responsive(ghost, key, obj[key]);
 66                 });
 67             }
 68 
 69             function compile(node, ghost) {
 70                 var re = /{{(.*)}}/;
 71                 if (node.nodeType == 1) {
 72                     var attrs = node.attributes;
 73                     for (var i = 0, len = attrs.length; i < len; i++) {
 74                         if (attrs[i].nodeName == "v-model") {
 75                             var key = attrs[i].nodeValue;
 76                             node.addEventListener("keyup", function(ev) {
 77                                 ghost[key] = ev.target.value;
 78                             });
 79                             node.value = ghost[key];
 80                             node.removeAttribute("v-model");
 81                         }
 82                     }
 83                 }
 84                 if (node.nodeType == 3) {
 85                     if (re.test(node.nodeValue)) {
 86                         var key = RegExp.$1;
 87                         key = key.trim();
 88                         new Watcher(ghost, node, key);
 89                     }
 90                 }
 91             }
 92 
 93             function nodeToFragment(node, ghost) {
 94                 var flag = document.createDocumentFragment();
 95                 var childNode;
 96                 while (childNode = node.firstChild) {
 97                     compile(childNode, ghost);
 98                     flag.appendChild(childNode);
 99                 }
100                 return flag;
101             }
102 
103             function Ghost(opt) {
104                 this.data = opt.data;
105                 var data = this.data;
106                 observe(data, this);
107                 var id = opt.el;
108                 var obox = document.querySelector(id);
109                 var node = nodeToFragment(obox, this);
110                 obox.appendChild(node);
111             }
112             var ogt = new Ghost({
113                 el: "#box",
114                 data: {
115                     content: "huanying2015",
116                     msg: "ahifahha",
117                 }
118             });
119         }
120     </script>
121 </head>
122 <body>
123     <div class="box" id="box">
124         <input type="text" id="tet" v-model="content"><br> {{content}}
125     </div>
126 </body>
127 </html>

运行结果:

原文地址:https://www.cnblogs.com/huanying2015/p/8540475.html