上一篇文章实现了数据的双向绑定,这一篇来实现v-bind, v-on指令以及methods等。
1. v-model语法糖指令拆分处理
1.1 首先来修改判断是否是指令的函数
isDirective(attrName){
return attrName.startsWith('v-on:')||attrName.startsWith('v-')
}
1.2 再修改使用指令对应工具函数的部分
compileElement(node){
let attributes = node.attributes; //某个元素的属性节点
[...attributes].forEach((attr)=>{
let { name, value: expr } = attr
//判断是否是指令
if(this.isDirective(name)){
let [directive, value] = name.substring(2).split(':')
if( directive === 'on' ) directive = value //2.添加 事件
if( directive === 'bind' ) directive = value //2.添加 自定义属性
CompilerUtil[directive]&&CompilerUtil[directive](node, expr, this.vm)//调用不同的处理指令
}
})
}
1.3 在CompilerUtil对象中添加,处理v-bind:value指令函数value
value()函数主要,就是根据表达式expr,获取在data上值,然后设置到input中
value(node,expr,vm){
let fn = this.updater['modelUpdater']
let content = this.getVal(vm, expr)
new Watcher(vm, expr, (newVal) => {
fn(node, newVal)
})
fn(node, content)
}
1.4 在CompilerUtil对象中添加,处理v-on:input指令函数input
input()函数,主要通过监听输入框的input方法,获取输入的值,设置到表达式(student.name = $event.target.value)左边
input(node,expr,vm){
let funcName = arguments.callee.name
let func = ($event)=>{
let [exprLeft, exprRight] = expr.indexOf('=')>-1 ? expr.split('=') : [expr,'$event.target.value']
// console.log(exprLeft,exprRight) // student.name , $event.target.value
let value = new Function('$event','return ' + exprRight)($event)
this.setVal(vm, exprLeft.trim(), value) // exprLeft 会存在空格
}
node.addEventListener(funcName, func, false)
}
到此,已经实现了v-bind:value和v-on:input指令,再改造下v-model指令就比较简单了
//处理v-model指令
model(node,expr,vm){
this.value(node,expr,vm)
this.input(node,expr,vm)
}
2. methods
接下来实现methods
2.1 先修改html部分,绑定之前在methods中,写好的changeInputValue函数
input type="text" v-bind:value="student.name" v-on:input="changeInputValue" >
此时可以思考,changeInputValue函数应该在什么时候执行呢?其实就是输入框input函数执行的时候;
修改input函数
input(node, expr, vm){
let funcName = arguments.callee.name
let func = ($event)=>{
let fn = vm.$methods[expr] // 2.1添加
// console.log(expr);
if(!fn){// 2.1添加
let [exprLeft, exprRight] = expr.indexOf('=')>-1 ? expr.split('=') : [expr,'$event.target.value']
// console.log(exprLeft,exprRight) // student.name , $event.target.value
let value = new Function('$event','return ' + exprRight)($event)
//调用setVal方法,设置数据
this.setVal(vm, exprLeft.trim(), value) // 找了好久 exprLeft 有空格
}else{// 2.1添加
fn && fn.call(vm,$event)
}
}
node.addEventListener(funcName, func, false)
}
2.2 v-on:click实现
先添加一个按钮绑定addAge函数
<button v-on:click="addAge">过年了,大一岁</button>
在CompilerUtil对象中添加处理v-bind:click指令函数click
主要就是获取methods中的函数,然后监听click事件在执行这个函数
//处理v-bind:click指令
click(node,expr,vm){
let funcName = arguments.callee.name
let func = ($event)=>{
let fn = vm.$methods[expr]
fn && fn.call(vm,$event)
}
node.addEventListener(funcName, func, false)
}