• vue数据双向绑定原理-解析器Complie



    ​​
    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. 基本完善了

  • 相关阅读:
    Luogu P2181 对角线 简单数学,细节
    vscode 配置C、C++环境,编写运行C、C++(转)
    用家庭电脑架设minecraft服务器
    用阿里云架设我的世界(minecraft)服务器
    在线数独
    数学物理笔记
    复活的asdf1229
    test
    GitHub从小白到精通(第一章 初识)
    抛砖引玉,浅讲Handler和线程的关系
  • 原文地址:https://www.cnblogs.com/chuchur/p/9397003.html
Copyright © 2020-2023  润新知