先看一个基础的vue绑定事件的代码
<template>
<div v-on:click="test" v-on:mousedown="test1('a',2)" v-on:mouseup="test2($event,'a',b)"></div>
</template>
<script>
export default Vue.extend({
data: () => {
return {
b: null
}
},
methods:{
test(){
},
test1(param1,param2){
},
test2(event,param1,param2){
}
}
});
</script>
这个没什么好说的v-on:click=“test”,其实就是click执行的test方法
但是后面两个test1(‘a’,2)和test2($ event,‘a’,b)
其实是在模板编译时被豁免了(想不出其他词汇了)
因为按照表达式编译test1(‘a’,2)和test2($ event,‘a’,b)其实在编译时就是会执行对应的test1和test2函数的,但实际上并没有执行,因为v-on在模板编译时,将两者编译成了类似如下的代码
//test1
function(){
return function(){
//context是vue上下文
context.test1('a',2)
}
}
//test2
function(){
return function(){
//context是vue上下文
context.test2(window.Event,'a',context.b)
}
}
其实就是最终编译出来的表达式其实v-on:后面还是函数而不是函数执行的表达式
这次编译vue做了两件事,
1.表达式编译
2.利用闭包将函数执行表达式编译成函数
防抖函数debounce和节流函数throttle,网上一抓一大把,此处不讨论,
此处讨论的是为何在vue指令中实现,
最大的原因,还是懒啊。。。
谁也不想每次都import这两个函数进来
有人之前实现过这个指令的,但是效果很差,
如何在Vue3中实现自定义指令(超详细!)
为啥效果很差。。。。
看最后他是怎么调用的就知道了
<input v-throttle="{fn: handleInput, event: 'input', delay: 1000}" />
有两个问题,1是如果需要handleInput传入参数的情况时,没法传入额外参数
2.本来简洁的v-on:input="handleInput"成了这鸟样-=
因此理想状态下的实现方式应该时这样的
<input v-throttle:input:300="test1('a',2)" v-debounce:click:500="test2($event,'a',b)" />
v-throttle:input:300表示节流没300毫秒执行一次input事件绑定的函数,而且支持传入参数
v-debounce:click:500 表示间隔500毫秒后才执行click事件的函数
throttle和debounce函数网上自己找吧。用的是vue-property-decorator写法
function analysisExpression(_this,expression){
let fName,args;
if(expression.indexOf("(")>-1){
fName = expression.substr(0,expression.indexOf("("));
let argStr = expression.substr(expression.indexOf("(")+1,expression.indexOf(")")-expression.indexOf("(")-1);
args=argStr.trim().length>0?argStr.split(","):[];
args = args.map(v=>{
if((v.startsWith("'") && v.endsWith("'"))||(v.startsWith("\"") && v.endsWith("\""))){
return v.substr(1,v.length-2);
}
if(!isNaN(v)){
return Number(v);
}
if(v==null||v==undefined){
return v;
}
if(v.toLowerCase()==='$event'){
return v.toLowerCase();
}
if(isNaN(v)){
let firstName = v.split(".")[0].trim();
if(window[firstName]){
return eval(v);
}
else{
return _this[v];
}
}
})
}
else{
fName=expression;
args=[];
}
return {
fun:_this[fName],
args:args
}
}
function bindDirective(fun: Function) {
return (el, binding, vnode) => {
let expression = binding.expression;
console.log(expression);
if (expression) {
let args = (binding.arg || 'click:500').split(":");
let event = args[0] || 'click';
let t = parseInt(args[1] || '500');
let res = analysisExpression(vnode.context,expression.trim());
console.log(res);
el.addEventListener(event, fun((e) => {
res.fun.apply(vnode.componentInstance,res.args.map(v=>{
if(v==='$event'){
return e;
}
return v;
}));
}, t));
}
}
}
@Component({
directives: {
debounce: {
bind: bindDirective(debounce)
},
throttle:{
bind: bindDirective(throttle)
},
throttle1:{
bind: bindDirective(throttle1)
}
}
})
代码中最关键的,也是最核心的就是analysisExpression,就是对指令的表达式的解析,目前只是最基础的解析,v-on的解析vue没有开放对应的api,其实可以直接借助那个解析函数的,另外还有就是编译时的豁免权,这个代码有个致命的问题,就是
v-throttle:input:300=“test1(‘a’,2)” v-debounce:click:500=“test2($event,‘a’,b)”
会直接执行test1和test2函数,因为没有进行编译豁免。。。
目前就这两点。。。。