• 【xinsir】vue双向数据绑定简易demo


      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>
  • 相关阅读:
    win10系统打印图片中间空白的解决办法
    DELPHI SOKET 编程--使用TServerSocket和TClientSocket
    因为未启用行移动功能 不能闪回表
    oracle闪退(回退)功能
    查看oracle数据库的数据文件的目录
    Oracle
    Delphi : keydown与keypress的区别,组合键
    Delphi Xe 中如何把日期格式统一处理,玩转 TDatetime
    移除 IIS 的各种头信息
    sql server 通过 sql 查询数据库状态
  • 原文地址:https://www.cnblogs.com/xinsir/p/12161377.html
Copyright © 2020-2023  润新知