Proxy 实现双向绑定

Proxy 用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.   <meta charset=“utf-8”>
  5.   <title>Proxy双向绑定</title>
  6. </head>
  7. <body>
  8.   <div id=“app”>
  9.     <input type=“text” v-model=“count”>
  10.     <div v-bind=“count”></div>
  11.   </div>
  12.   <script type=“text/javascript”>
  13.     class CustomFunc {
  14.       constructor(options) {
  15.         this.$el = document.querySelector(options.el);
  16.         this._binding = {};
  17.         this._observer(options.data);
  18.         this._compile(this.$el);
  19.       }
  20.       _pushWatcher(watcher) {
  21.         if (!this._binding[watcher.key]) {
  22.           this._binding[watcher.key] = [];
  23.         }
  24.         this._binding[watcher.key].push(watcher);
  25.       }
  26.       /*
  27.        observer的作用是能够对所有的数据进行监听操作,通过使用Proxy对象
  28.        中的set方法来监听,如有发生变动就会拿到最新值通知订阅者。
  29.       */
  30.       _observer(datas) {
  31.         const me = this;
  32.         const handler = {
  33.           set(target, key, value) {
  34.             const rets = Reflect.set(target, key, value);
  35.             me._binding[key].map(item => {
  36.               item.update();
  37.             });
  38.             return rets;
  39.           }
  40.         };
  41.         this.$data = new Proxy(datas, handler);
  42.       }
  43.       /*
  44.        指令解析器,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相对应的更新函数
  45.       */
  46.       _compile(root) {
  47.         const nodes = Array.prototype.slice.call(root.children);
  48.         const data = this.$data;
  49.         nodes.map(node => {
  50.           if (node.children && node.children.length) {
  51.             this._compile(node.children);
  52.           }
  53.           const $input = node.tagName.toLocaleUpperCase() === “INPUT”;
  54.           const $textarea = node.tagName.toLocaleUpperCase() === “TEXTAREA”;
  55.           const $vmodel = node.hasAttribute(‘v-model’);
  56.           // 如果是input框 或 textarea 的话,并且带有 v-model 属性的
  57.           if (($vmodel && $input) || ($vmodel && $textarea)) {
  58.             const key = node.getAttribute(‘v-model’);
  59.             this._pushWatcher(new Watcher(node, ‘value’, data, key));
  60.             node.addEventListener(‘input’, () => {
  61.               data[key] = node.value;
  62.             });
  63.           }
  64.           if (node.hasAttribute(‘v-bind’)) {
  65.             const key = node.getAttribute(‘v-bind’);
  66.             this._pushWatcher(new Watcher(node, ‘innerHTML’, data, key));
  67.           }
  68.         });
  69.       }
  70.     }
  71.     /*
  72.      watcher的作用是 链接Observer 和 Compile的桥梁,能够订阅并收到每个属性变动的通知,
  73.      执行指令绑定的响应的回调函数,从而更新视图。
  74.     */
  75.     class Watcher {
  76.       constructor(node, attr, data, key) {
  77.         this.node = node;
  78.         this.attr = attr;
  79.         this.data = data;
  80.         this.key = key;
  81.       }
  82.       update() {
  83.         this.node[this.attr] = this.data[this.key];
  84.       }
  85.     }
  86.   </script>
  87.   <script type=“text/javascript”>
  88.     new CustomFunc({
  89.       el: ‘#app’,
  90.       data: {
  91.         count: 0
  92.       }
  93.     });
  94.   </script>
  95. </body>
  96. </html>

本文链接地址: Proxy 实现双向绑定

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注