1. 程式人生 > >vue數據雙向綁定原理-解析器Complie

vue數據雙向綁定原理-解析器Complie

ava div des for lse 遍歷 `` dde move

技術分享圖片
??
1)vue數據雙向綁定原理-observer
?2)vue數據雙向綁定原理-wather?
3)vue數據雙向綁定原理-解析器Complie

vue數據雙向綁定原理, 和簡單的實現,本文將實現mvvm的模板指令解析器

上一步實現了簡單數據綁定, 最後實現解析器,來解析 v-model ,v-on:click等指令,和 {{}} 模板數據.解析器Compile實現步驟:

1.解析模板指令,並替換模板數據,初始化視圖

2.將模板指令對應的節點綁定對應的更新函數,初始化相應的訂閱器

為了解析模板,首先需要獲取到dom元素,然後對含有dom元素上含有指令的節點進行處理,因此這個環節需要對dom操作比較頻繁,所有可以先建一個fragment片段,將需要解析的dom節點存入fragment片段裏再進行處理:

function node2Fragment(el) {
        var fragment = document.createDocumentFragment(), child;
        // 將原生節點拷貝到fragment
        while (child = el.firstChild) {
            fragment.appendChild(child);
        }
        return fragment;
}

接下來 渲染‘{{}}‘模板

//Compile
function Compile(el, vm) {
   this.$vm = vm
   this.$el = this.isElementNode(el) ? el : document.querySelector(el);
   if (this.$el) {
      this.$fragment = this.node2Fragment(this.$el);
      this.init();
      this.$el.appendChild(this.$fragment);
   }
};

Compile.prototype = {
   init: function () {
      this.compileElement(this.$fragment);
   },
   node2Fragment: function (el) {
      //...
   },
   //編譯模板
   compileElement: function (el) {
      var childNodes = el.childNodes,
         self = this;
      [].slice.call(childNodes).forEach(function (node) {
         var text = node.textContent;
         var reg = /{{(.*)}}/; //表達式文本
         //按元素節點方式編譯
         if (self.isElementNode(node)) {
            self.compile(node);
         } else if (self.isTextNode(node) && reg.test(text)) {
            self.compileText(node, RegExp.$1);
         };
         //遍歷編譯子節點
         if (node.childNodes && node.childNodes.length) {
            self.compileElement(node);
         };
      });
   },
   isElementNode: function (node) {
      return node.nodeType == 1;
   },
   isTextNode: function (node) {
      return node.nodeType == 3;
   },
   compileText: function (node, exp) {
      var self = this;
      var initText = this.$vm[exp];
      this.updateText(node, initText);
      new Watcher(this.$vm, exp, function (value) {
         self.updateText(node, value);
      });
   },
   updateText: function (node, value) {

      node.textContent = typeof value == ‘undefined‘ ? ‘‘ : value;
   },
}

處理解析指令對相關指令進行函數綁定,

Compile.prototype = {
   ......
   isDirective: function (attr) {
      return attr.indexOf(‘v-‘) == 0;
   },
   isEventDirective: function (dir) {
      return dir.indexOf(‘on:‘) === 0;
   },
   //處理v-指令
   compile: function (node) {
      var nodeAttrs = node.attributes,
         self = this;
      [].slice.call(nodeAttrs).forEach(function (attr) {
         // 規定:指令以 v-xxx 命名
         // 如 <span v-text="content"></span> 中指令為 v-text
         var attrName = attr.name; // v-text
         if (self.isDirective(attrName)) {
            var exp = attr.value; // content
            var dir = attrName.substring(2); // text
            if (self.isEventDirective(dir)) {
               // 事件指令, 如 v-on:click
               self.compileEvent(node, self.$vm, exp, dir);
            } else {
               // 普通指令如:v-model,v-html,當前只處理v-model
               self.compileModel(node, self.$vm, exp, dir);
            }
            //處理完畢要幹掉 v-on:,v-model 等元素屬性
            node.removeAttribute(attrName)
         }
      });
   },
   compileEvent: function (node, vm, exp, dir) {
      var eventType = dir.split(‘:‘)[1];
      var cb = vm.$options.methods && vm.$options.methods[exp];
      if (eventType && cb) {
         node.addEventListener(eventType, cb.bind(vm), false);
      }
   },
   compileModel: function (node, vm, exp, dir) {
      var self = this;
      var val = this.$vm[exp];
      this.updaterModel(node, val);
      new Watcher(this.$vm, exp, function (value) {
         self.updaterModel(node, value);
      });
      node.addEventListener(‘input‘, function (e) {
         var newValue = e.target.value;
         if (val === newValue) {
            return;
         }
         self.$vm[exp] = newValue;
         val = newValue;
      });
   },
   updaterModel: function (node, value, oldValue) {
      node.value = typeof value == ‘undefined‘ ? ‘‘ : value;
   },
}

最後再關聯起來

function Vue(options) {
    .....
    observe(this.data, this);    
    this.$compile = new Compile(options.el || document.body, this)
    return this;
}

來嘗試下效果
```html


{{name}}



{{name}}




OK. 基本完善了

vue數據雙向綁定原理-解析器Complie