vue数据双向绑定原理-complie

邱秋1年前 22:10
vue数据双向绑定原理, 和简单的实现,本文将实现mvvm的模板指令解析器

​​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-->
<div id="app">
     <h2>{{name}}</h2>
    <input v-model="name">
    <h1>{{name}}</h1>
    <button v-on:click="test">click here!</button>
</div>
<script>
new Vue({
    el: '#app',
    data: {
        name: 'chuchur',
        age: 29
    },
    methods: {
        test() {
            this.name = 'My name is chuchur'
        }
    }
})
</script>

OK. 基本完善了

本文于1年前 22:10发布在code分类下,当前已被围观1610次

相关标签:vue,

永久地址:https://www.chuchur.com/article/vue-mvvm-complie

版权声明: 自由转载-署名-非商业性使用

匿名Mac OSChrome63火星#12
不错
匿名用户