zoukankan      html  css  js  c++  java
  • (尚052)+(尚053) Vue_源码分析_模板解析_大括号表达式

    1.页面展示,03_模板解析_表达式_mvvm.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>03_模板解析_表达式_vue</title>
    </head>
    <body>
    <div id="test">
    <p>{{name}}</p>
    </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" src="./js/vue.js"></script>
    <script type="text/javascript">
    new MVVM({
    el:'#test',
    data:{
    name:'钟会'
    }
    })
    </script>


    </body>
    </html>
    ==============================================================================================
    1.2 02_数据代理_vue.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>02_数据代理_vue</title>
    </head>
    <body>
    <div id="test"></div>
    <script type="text/javascript" src="./js/vue.js"></script>
    <script type="text/javascript">
    const vm=new Vue({
    el:'#test',
    data:{
    name:'赵云'
    }
    })
    console.log(vm.name,vm) //赵云 //vm代理对data数据的读操作
    vm.name='关羽' //现在编码的时候改变的是vm中的name,但是vm不存name值,数据在data中 //vm代理对data数据的写操作
    //如何验证数据在data中?
    console.log(vm._data.name,vm.name) //关羽


    </script>
    </body>
    </html>

    ==============================================================================================
    2.compile.js (谷歌浏览器调试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;
    // 判断是否是指令属性
    if (me.isDirective(attrName)) {
    // 得到表达式(属性值): test
    var exp = attr.value;
    // 得到指令名: on:click
    var dir = attrName.substring(2);
    // 事件指令
    if (me.isEventDirective(dir)) {
    // 解析事件指令
    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中): test(){}
    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;
    }
    };
    ============================================================================================
    3.mvvm.js (谷歌浏览器调试js)
    /*
    相关于Vue的构造函数
    */
    function MVVM(options) {
    // 将选项对象保存到vm
    this.$options = options;
    // 将data对象保存到vm和datq变量中
    var data = this._data = this.$options.data;
    //将vm保存在me变量中
    var me = this;
    // 遍历data中所有属性
    Object.keys(data).forEach(function (key) { // 属性名: name
    // 对指定属性实现代理
    me._proxy(key);
    });

    // 对data进行监视
    observe(data, this);

    // 创建一个用来编译模板的compile对象
    this.$compile = new Compile(options.el || document.body, this)
    }

    MVVM.prototype = {
    $watch: function (key, cb, options) {
    new Watcher(this, key, cb);
    },

    // 对指定属性实现代理
    _proxy: function (key) {
    // 保存vm
    var me = this;
    // 给vm添加指定属性名的属性(使用属性描述)
    Object.defineProperty(me, key, {
    configurable: false, // 不能再重新定义
    enumerable: true, // 可以枚举
    // 当通过vm.name读取属性值时自动调用
    get: function proxyGetter() {
    // 读取data中对应属性值返回(实现代理读操作)
    return me._data[key];
    },
    // 当通过vm.name = 'xxx'时自动调用
    set: function proxySetter(newVal) {
    // 将最新的值保存到data中对应的属性上(实现代理写操作)
    me._data[key] = newVal;
    }
    });
    }
    };
  • 相关阅读:
    理解MySQL——索引与优化
    Android中shape的使用
    Android之Camera控制拍照
    android的fragments管理
    android的fragment基本介绍
    android的animator
    android软键盘弹出隐藏的监听
    android平板Home键的监听
    android jsonarray
    android 应用静默自启动的解决方法
  • 原文地址:https://www.cnblogs.com/curedfisher/p/12315691.html
Copyright © 2011-2022 走看看