前端前端
fabric
算法
jsBlock
styleBlock
svgBlock
工具其他
Vue、React相关
webgl
GitHub
fabric
算法
jsBlock
styleBlock
svgBlock
工具其他
Vue、React相关
webgl
GitHub
  • 1_1_nextjs项目开发
  • 1_2_nextjs项目部署
  • React
  • React使用中遇到的一些问题
  • React构建create-react-app简单说明
  • ubuntu使用
  • vue.init
  • vue.use
  • vuex源码解析
  • Vue使用中遇到的一些问题
  • vue梳理
  • watcher&&dep
  • 代码片段
  • 打包
  • 简单双向绑定
  • 纯函数

简单双向绑定

[转][https://github.com/bison1994/two-way-data-binding/blob/master/index.html]

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Two-way-data-binding</title>
  </head>
  <body>
    <div id="app">
      <input type="text" v-model="text" />
      {{ text }}
    </div>

    <script>
      function observe(obj, vm) {
        Object.keys(obj).forEach(function (key) {
          defineReactive(vm, key, obj[key]);
        });
      }

      function defineReactive(obj, key, val) {
        var dep = new Dep();

        Object.defineProperty(obj, key, {
          get: function () {
            // 添加订阅者 watcher 到主题对象 Dep
            if (Dep.target) dep.addSub(Dep.target);
            return val;
          },
          set: function (newVal) {
            if (newVal === val) return;
            val = newVal;
            // 作为发布者发出通知
            dep.notify();
          },
        });
      }

      function nodeToFragment(node, vm) {
        var flag = document.createDocumentFragment();
        var child;
        // 许多同学反应看不懂这一段,这里有必要解释一下
        // 首先,所有表达式必然会返回一个值,赋值表达式亦不例外
        // 理解了上面这一点,就能理解 while (child = node.firstChild) 这种用法
        // 其次,appendChild 方法有个隐蔽的地方,就是调用以后 child 会从原来 DOM 中移除
        // 所以,第二次循环时,node.firstChild 已经不再是之前的第一个子元素了
        while ((child = node.firstChild)) {
          compile(child, vm);
          flag.appendChild(child); // 将子节点劫持到文档片段中
        }

        return flag;
      }

      function compile(node, vm) {
        var reg = /\{\{(.*)\}\}/;
        // 节点类型为元素
        if (node.nodeType === 1) {
          var attr = node.attributes;
          // 解析属性
          for (var i = 0; i < attr.length; i++) {
            if (attr[i].nodeName == "v-model") {
              var name = attr[i].nodeValue; // 获取 v-model 绑定的属性名
              node.addEventListener("input", function (e) {
                // 给相应的 data 属性赋值,进而触发该属性的 set 方法
                vm[name] = e.target.value;
              });
              // node.value = vm[name]; // 将 data 的值赋给该 node
              node.removeAttribute("v-model");
            }
          }

          new Watcher(vm, node, name, "input");
        }
        // 节点类型为 text
        if (node.nodeType === 3) {
          if (reg.test(node.nodeValue)) {
            var name = RegExp.$1; // 获取匹配到的字符串
            name = name.trim();

            new Watcher(vm, node, name, "text");
          }
        }
      }

      function Watcher(vm, node, name, nodeType) {
        Dep.target = this;
        this.name = name;
        this.node = node;
        this.vm = vm;
        this.nodeType = nodeType;
        this.update();
        Dep.target = null;
      }

      Watcher.prototype = {
        update: function () {
          this.get();
          if (this.nodeType == "text") {
            this.node.nodeValue = this.value;
          }
          if (this.nodeType == "input") {
            this.node.value = this.value;
          }
        },
        // 获取 data 中的属性值
        get: function () {
          this.value = this.vm[this.name]; // 触发相应属性的 get
        },
      };

      function Dep() {
        this.subs = [];
      }

      Dep.prototype = {
        addSub: function (sub) {
          this.subs.push(sub);
        },

        notify: function () {
          this.subs.forEach(function (sub) {
            sub.update();
          });
        },
      };

      function Vue(options) {
        this.data = options.data;
        var data = this.data;

        observe(data, this);

        var id = options.el;
        var dom = nodeToFragment(document.getElementById(id), this);

        // 编译完成后,将 dom 返回到 app 中
        document.getElementById(id).appendChild(dom);
      }

      var vm = new Vue({
        el: "app",
        data: {
          text: "hello world",
        },
      });
    </script>
  </body>
</html>

Edit this page
最近更新:: 2021/5/26 17:52
Contributors: wuhui
Prev
打包
Next
纯函数