zoukankan      html  css  js  c++  java
  • (尚054)Vue_源码分析_模板解析_事件指令

     指令解析,分两种:

    1.事件指令;

    2.一般指令;

    ==========================================

    04_模板解析_事件指令.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>04_模板解析_事件指令_my</title>
    </head>
    <body>
    <div id="test">
    <p>{{name}}</p>
    <button v-on:click="show">提示</button>
    </div>

    <script type="text/javascript" src="js/mvvm/compile.js"></script>
    <script type="text/javascript" src="js/mvvm/mvvm.js"></script>
    <script type="text/javascript" src="js/mvvm/observer.js"></script>
    <script type="text/javascript" src="js/mvvm/watcher.js"></script>
    <script type="text/javascript">
    new MVVM({
    el:'#test',
    data:{
    name:'赵云:吾乃常山赵子龙也'
    },
    methods:{
    show(){
    alert(this.name)
    }
    }
    })

    </script>
    </body>
    </html>
    ======================================================================================
    l浏览器调试所用js
    compile.js
    function Compile(el, vm) {
    // 保存vm
    this.$vm = vm;
    // 保存el元素
    this.$el = this.isElementNode(el) ? el : document.querySelector(el);
    // 如果el元素存在
    if (this.$el) {
    // 1. 取出el中所有子节点, 封装在一个framgment对象中
    this.$fragment = this.node2Fragment(this.$el);
    // 2. 编译fragment中所有层次子节点
    this.init();
    // 3. 将fragment添加到el中
    this.$el.appendChild(this.$fragment);
    }
    }

    Compile.prototype = {
    node2Fragment: function (el) {
    var fragment = document.createDocumentFragment(),
    child;

    // 将原生节点拷贝到fragment
    while (child = el.firstChild) {
    fragment.appendChild(child);
    }

    return fragment;
    },

    init: function () {
    // 编译fragment
    this.compileElement(this.$fragment);
    },

    compileElement: function (el) {
    // 得到所有子节点
    var childNodes = el.childNodes,
    // 保存compile对象
    me = this;
    // 遍历所有子节点
    [].slice.call(childNodes).forEach(function (node) {
    // 得到节点的文本内容
    var text = node.textContent;
    // 正则对象(匹配大括号表达式)
    var reg = /{{(.*)}}/; // {{name}}
    // 如果是元素节点
    if (me.isElementNode(node)) {
    // 编译元素节点的指令属性
    me.compile(node);
    // 如果是一个大括号表达式格式的文本节点
    } else if (me.isTextNode(node) && reg.test(text)) {
    // 编译大括号表达式格式的文本节点
    me.compileText(node, RegExp.$1); // RegExp.$1: 表达式 name
    }
    // 如果子节点还有子节点
    if (node.childNodes && node.childNodes.length) {
    // 递归调用实现所有层次节点的编译
    me.compileElement(node);
    }
    });
    },

    compile: function (node) {
    // 得到所有标签属性节点
    var nodeAttrs = node.attributes,
    me = this;
    // 遍历所有属性
    [].slice.call(nodeAttrs).forEach(function (attr) {
    // 得到属性名: v-on:click
    var attrName = attr.name;
    // 判断是否是指令属性(是否有v-)
    if (me.isDirective(attrName)) {
    // 得到表达式(属性值): test
    var exp = attr.value;
    // 得到指令名: on:click
    var dir = attrName.substring(2);
    // 判断是否是事件指令(是否是on开头)
    if (me.isEventDirective(dir)) {
    // 解析处理事件指令
          //node--节点, me.$vm--vm, exp--表达式, dir--指令名on:click
            compileUtil.eventHandler(node, me.$vm, exp, dir);
    // 普通指令
    } else {
    // 解析普通指令
    compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
    }

    // 移除指令属性
    node.removeAttribute(attrName);
    }
    });
    },

    compileText: function (node, exp) {
    // 调用编译工具对象解析
    compileUtil.text(node, this.$vm, exp);
    },

    isDirective: function (attr) {
    return attr.indexOf('v-') == 0;
    },

    isEventDirective: function (dir) {
    return dir.indexOf('on') === 0;
    },

    isElementNode: function (node) {
    return node.nodeType == 1;
    },

    isTextNode: function (node) {
    return node.nodeType == 3;
    }
    };

    // 指令处理集合
    var compileUtil = {
    // 解析: v-text/{{}}
    text: function (node, vm, exp) {
    this.bind(node, vm, exp, 'text');
    },
    // 解析: v-html
    html: function (node, vm, exp) {
    this.bind(node, vm, exp, 'html');
    },

    // 解析: v-model
    model: function (node, vm, exp) {
    this.bind(node, vm, exp, 'model');

    var me = this,
    val = this._getVMVal(vm, exp);
    node.addEventListener('input', function (e) {
    var newValue = e.target.value;
    if (val === newValue) {
    return;
    }

    me._setVMVal(vm, exp, newValue);
    val = newValue;
    });
    },

    // 解析: v-class
    class: function (node, vm, exp) {
    this.bind(node, vm, exp, 'class');
    },

    // 真正用于解析指令的方法
    bind: function (node, vm, exp, dir) {
    /*实现初始化显示*/
    // 根据指令名(text)得到对应的更新节点函数
    var updaterFn = updater[dir + 'Updater'];
    // 如果存在调用来更新节点
    updaterFn && updaterFn(node, this._getVMVal(vm, exp));

    // 创建表达式对应的watcher对象
    new Watcher(vm, exp, function (value, oldValue) {/*更新界面*/
    // 当对应的属性值发生了变化时, 自动调用, 更新对应的节点
    updaterFn && updaterFn(node, value, oldValue);
    });
    },

    // 事件处理
    eventHandler: function (node, vm, exp, dir) {
    // 得到事件名/类型: click
    var eventType = dir.split(':')[1],
    //从methods中得到表达式所对应的函数(事件回调函数)
    fn = vm.$options.methods && vm.$options.methods[exp];
    // 如果都存在
    if (eventType && fn) {
    // 绑定指定事件名和回调函数的DOM事件监听, 将回调函数中的this强制绑定为vm
    node.addEventListener(eventType, fn.bind(vm), false);
    }
    },

    // 得到表达式对应的value
    _getVMVal: function (vm, exp) {
    var val = vm._data;
    exp = exp.split('.');
    exp.forEach(function (k) {
    val = val[k];
    });
    return val;
    },

    _setVMVal: function (vm, exp, value) {
    var val = vm._data;
    exp = exp.split('.');
    exp.forEach(function (k, i) {
    // 非最后一个key,更新val的值
    if (i < exp.length - 1) {
    val = val[k];
    } else {
    val[k] = value;
    }
    });
    }
    };

    // 包含多个用于更新节点方法的对象
    var updater = {
    // 更新节点的textContent
    textUpdater: function (node, value) {
    node.textContent = typeof value == 'undefined' ? '' : value;
    },

    // 更新节点的innerHTML
    htmlUpdater: function (node, value) {
    node.innerHTML = typeof value == 'undefined' ? '' : value;
    },

    // 更新节点的className
    classUpdater: function (node, value, oldValue) {
    var className = node.className;
    className = className.replace(oldValue, '').replace(/s$/, '');

    var space = className && String(value) ? ' ' : '';

    node.className = className + space + value;
    },

    // 更新节点的value
    modelUpdater: function (node, value, oldValue) {
    node.value = typeof value == 'undefined' ? '' : value;
    }
    };
    =======================================================================================
    总结:
    模板解析(2): 事件指令解析
    1) 从指令名中取出事件名 --  (click)
    2) 根据指令的值(表达式)从 methods 中得到对应的事件处理函数对象  --  (show)    
    3) 给当前元素节点绑定指定事件名和回调函数的 dom 事件监听   
    4) 指令解析完后, 移除此指令属性


  • 相关阅读:
    销售类
    语法
    编辑技巧 word
    assert
    游戏摘录
    游戏类链接 财富导图
    读书笔记之C# delegate
    c# socket传输struct类型
    关于wcf中一些重要词语解释
    asp.net CROSS-PAGE POSTING
  • 原文地址:https://www.cnblogs.com/curedfisher/p/12315743.html
Copyright © 2011-2022 走看看