下个套
native
这个修饰符我平时很少用到,但是在公司项目中遇到过,那就得把它弄清楚。
为了把它和常规的 v-on
进行比较,我准备了一个很简单的组件:
1 | <template> |
这个组件里面什么都没有,先把它叫做 Test,那么现在有个父组件里面用到了 Test,<Test @click="handleClick" />
,理想情况是点击 Test 之后打印出 123,那么实际结果是什么呢?
解答
如果你觉得能打印出 123,那么我推荐你去看一下我之前写的《对 Vue 事件机制错误理解的纠正》,实际上 Test 被点击后是没有任何反应的。原因在于 Vue 中事件的回调和触发都是需要你自己定义的,说具体点就是回调需要通过 v-on
提供,与此同时,在给组件设置事件的时候,触发条件也得你来设置。
比如在刚刚的例子中,父组件给子组件的 click
事件设置了一个回调 handleClick,然而子组件里面根本没有触发这个事件,所以回调没有被调用(为什么不写成点击无效),要实现这个需求,正确的写法是:
1 | <template> |
这里监听了 div 的点击操作,在点击后让 Test 本身调用 \$emit(不理解 \$emit 运行机制的也可以参考《对 Vue 事件机制错误理解的纠正》),去执行父组件传来的回调函数 handleClick。
为什么不写成点击无效
从刚刚的例子可以看出,组件事件回调的设置者是你,触发的设置者也是你,实际上 @click
和点击操作已经没有直接联系了,比如说你可以写成 @input="handler"
,在子组件中使用 @click="$emit('input')"
,这也能完成点击执行 handler 的操作,例子中只是触发了一个叫做 click 的回调函数罢了。
事实上,组件的事件名称是可以自定义的,只要让他在合适的时机触发就行了,当然了,实际开发环境中不建议乱起名字。
绕回来了
那么,native
修饰符有什么用呢,v-on
本身都那么灵活了,还需要修饰符吗?native
的作用是为组件的根元素绑定原生事件,注意是根元素,还是刚刚的例子,要做一个点击打印的效果,如果直接用 native
,那么可以写成:
1 | <Test @click.native="handleClick" /> |
区别就出来了,子组件里面什么事件相关的内容都没有,但仍然能对点击操作做出反馈,因为父组件给子组件的根元素(也就是 div),绑定了个原生的点击事件。
要注意的是,这里绑的是原生事件,如果把 `@click.native改成
@agssff.native` 这种瞎打的事件名,是一点效果都没有的,因为它不是原生事件。
那突发奇想,在子组件里调用 $emit('agssff')
呢?实际上也是没有效果的,因为 $emit
的运行机制,他会调用储存在 events
里的回调函数,native
将原生事件直接绑定在了根元素上,根本没有通过 events
,所以 $emit
也是无效的。
举一反三
对 Vue 的事件原理有了进一步理解之后,可以更好地解决一些实际问题了,比如跨组件传递 v-model。
现在有一个父组件 Parent 和子组件 Children,Children中有一个 input 元素,需要在父组件中通过 v-model 关联这个 input 元素,尽可能不要在子组件中引入新的响应式变量。
1 | <template> |
1 | <template> |
这就是完美的解法,父组件的 v-model 是一个由 v-bind
和 v-on
构成的语法糖,其中 v-on 向子组件的 events
中添加了一个名为 input 的函数,所以子组件可以直接通过 $emit('input')
来触发这个函数。
如果直接在 input 元素中写 v-model="text"
,会造成子组件修改父组件数据,这明显是不正确的。
还有一种常见的写法,是在子组件中设置一个中间变量,比如 value,使 value 的初始值等同于 text,再用 v-model="value"
做双向绑定,watch 监听 value变化……
这明显就把问题变得更加复杂了。