模板解析(1) 大括号解析

模板解析的基本流程
1)将el的所有子节点取出,添加到一个新建的文档fragment对象中
2)对fragment 中的所有层次子节点递归进行编译解析处理

  • 对大括号表达式文本节点进行解析
  • 对元素节点的指令属性进行解析
    *事件指令解析
  1. 根据正则对象得到匹配出的表达式字符串:子匹配/RegExp.$1
  2. 从data中取出表达式对应的属性值
    5) 将属性设置为文本节点的textContent
    一个大括号表达式如何显示:取出name,根据name取出对应的value,有了value设置到对应节点的context中
    模板:就是html嵌套了js代码,语法有两种:指令属性和表达式
    递归是一种特别的嵌套调用函数
    当使用了函数调用变化的函数名要使用 func[ dir + ‘updater’] dir是会变化的去调用func内部的函数。
querySelector() 方法返回文档中匹配指定 CSS 选择器的一个元素。

指令 = > 操作标签
1、init()是进行编译的

function Compile(el, vm) {
    this.$vm = vm; // 保存传来的vm的实例
    this.$el = this.isElementNode(el) ? el : document.querySelector(el); // 前者判断是否为元素节点 后者 5.通过querySelector(el) 去查找 存储的一定是Dom

    if (this.$el) { // 如果有el
        this.$fragment = this.node2Fragment(this.$el); // 1.将el元素的所有子节点保存到fragment
        this.init(); // 2.编译fragment中所有层次子节点
        this.$el.appendChild(this.$fragment); // 3.将编译好的fragment添加到el中
    }
}

2、真正编译是compileElement

init: function() {
    this.compileElement(this.$fragment);  // 编译指定元素(所有层次的子节点)
}

3、去便利每个子节点(递归调用)

compileElement: function(el) {
    var childNodes = el.childNodes, // 取出最外层的子节点
        me = this; // 保存compile实例

    [].slice.call(childNodes).forEach(function(node) { // 便利所有子节点(text/element)
        var text = node.textContent; // 得到节点的文本内容
        var reg = /\{\{(.*)\}\}/;  // 匹配大括号表达式 {{ name }}  () 小括号子匹配 是否取出name

        if (me.isElementNode(node)) {  // 判断节点是否为一个元素节点
            me.compile(node); // 用来编译元素节点属性  所有的指令的(只要是元素节点就编译)

        } else if (me.isTextNode(node) && reg.test(text)) { // 判断节点是否是大括号表达式的文本杰点
            me.compileText(node, RegExp.$1.trim());  // 编译大括号表达式文本节点
        }

        if (node.childNodes && node.childNodes.length) { // 子节点是否有子节点么?并且子节点的子节点长度是否大于0?
            me.compileElement(node); // 递归调用 实现所有层次节点的编译
        }
    });
}

4、compileElement调用 绑定名text

compileText: function(node, exp) {
    compileUtil.text(node, this.$vm, exp); // 19.编译工具对象  这个方法用来帮助我们编译我们编译v-text 和大括号表达式使用同一函数 exp表达式 $vm vm
}

5、compileUtil 根据传来的指令名选择绑定

var compileUtil = {
    // v-text {}
    text: function(node, vm, exp) {
        this.bind(node, vm, exp, 'text'); // 20.绑定数据 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) {
        var updaterFn = updater[dir + 'Updater']; //  得到更新节点的函数

        updaterFn && updaterFn(node, this._getVMVal(vm, exp)); // 调用函数更新节点

        new Watcher(vm, exp, function(value, oldValue) {  // 为更新做准备工作
            updaterFn && updaterFn(node, value, oldValue);
        });
    }

6、_getVMVal 为取值

// 从vm中拿到所对应的值
_getVMVal: function(vm, exp) { //  23.怕出现多个属性
    var val = vm; // 去到name所对应的值
    exp = exp.split('.'); // 表达式拆分
    exp.forEach(function(k) { // 一层一层取
        val = val[k];
    });
    return val;
}

7、通过updaterFn选择方法并赋值

var updater = { // 更新节点的textContent 值
    textUpdater: function(node, value) {
        node.textContent = typeof value == 'undefined' ? '' : value;  // 操作节点的text  24.最后赋值
    },

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

    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; // 操作节点的class
    },

    modelUpdater: function(node, value, oldValue) {
        node.value = typeof value == 'undefined' ? '' : value; // 操作节点的value
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值