1:模板编译(html中能够识别指令和大括号表达式)实现指定的功能
目的:编译大括号表达式和v-on等等的一些指令
<div id="test">
<p >{{name}}</p>
</div>
实现思路:找的指定的#test需要编译的模板,将#test取出#test中所有子节点, 封装在一个framgment对象中。例如:<div id="test"> <p >{{name}}</p></div> text节点,p元素节点,text节点,然后编译framgment的所有子节点(递归更找出子节点的子节点 p元素中有一个text节点里面有大括号表达式。
//framgment 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); } }); },
me.compileText(node, RegExp.$1); // RegExp.$1: 表达式 name
{{name}}实例就是找到了这个text节点,然后通过对象代理 vm.name//获取属性{{name}}就被解析出来了。
me.compile(node);编译元素节点判断里面是否具有v-开头的属性本质上是通过正则表达式匹配v-,根据attributeNode来寻找属性在匹配
2:指令解析<button v-on:click="test">测试</button>
来举例:同样首先通过上面类似的步骤找到<button v-on:click="test">测试</button>这个元素节点
if (me.isElementNode(node)) {
// 编译元素节点的指令属性
me.compile(node); //buuton元素进入此判断
// 如果是一个大括号表达式格式的文本节点
}
//button节点进入编译 node=button
compile: function (node) { // 得到所有标签属性节点 var nodeAttrs = node.attributes, //v-on:click="test"被获取 me = this; // 遍历所有属性 [].slice.call(nodeAttrs).forEach(function (attr) { // 得到属性名: v-on:click var attrName = attr.name; // // 判断是否是指令属性 if (me.isDirective(attrName)) { //isDirective: function (attr) { return attr.indexOf('v-') == 0; 这个函数是通过v-判断是否是指令属性 }// // 得到表达式(属性值): 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); } }); }, //添加事件监听(监听元素节点 button,vm对象,事件回调函数名 "test","on:click") compileUtil.eventHandler(node, me.$vm, exp, dir); // 事件处理 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); } }, // 解析: 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; }); }, // 真正用于解析指令的方法 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); }); },