Vue2 的事件绑定主要围绕 v-on 指令展开,简写为 @,回调函数需要定义在 Vue 实例的 methods 中,模板负责把事件名和回调关联起来。原文特别强调一个常见错误:methods 中的回调不要写成箭头函数,否则 this 不能按预期拿到 Vue 实例。下面按语法对比、参数传递、this 指向和 methods 挂载实现四个部分整理可落地的写法。
一、从原生事件绑定看 v-on
原生 JavaScript 常见的三种绑定方式如下:- let btn1 = document.getElementById('btn1');
- btn1.addEventListener('click', () => {
- console.log('hello1');
- });
- btn1.onclick = function() {
- console.log('hello2');
- };
- function demo1() {
- console.log('hello3');
- }
复制代码 Vue2 把事件绑定写成 v-on:事件名 或 @事件名。基本语法和简写形式可以这样写:- <button v-on:click='demo()'>按钮</button>
- <button @click='demo()'>按钮</button>
复制代码 这里的 demo 需要是 Vue 实例 methods 中定义的方法。原文给出的错误示例中,v-on:click=“alert('hello')”使用了 alert,v-on:click=“demo1()”使用了全局函数,这两者都不是 Vue 实例管理的方法,所以不能按 methods 回调的方式工作。正确方式是在 methods 配置项中定义回调,再在模板中调用。
二、事件参数传递规则
当既需要自定义参数又需要事件对象时,可以用 $event 占位符显式传入当前事件对象:- <input type='text' @input='fun(1, 2, 3, $event)' />
复制代码 如果只需要事件对象,可以省略小括号,直接写方法名:- <input type='text' @input='fun' />
复制代码 对应的 methods 可以这样写:- methods: {
- fun(a, b, c, e) {
- console.log(e.target.value);
- console.log(a, b, c);
- }
- }
复制代码 从原文示例可归纳:Vue 在调用回调时会自动传入当前事件对象;需要额外参数时用 $event 占位;不需要额外参数时省略小括号即可。输入框事件中可通过 e.target.value 读取当前输入值。
三、this 指向:普通函数与箭头函数的差异
普通函数写法下,methods 回调中的 this 指向 Vue 实例 vm,因此可以访问 data,也可以调用其他 methods:- methods: {
- add() {
- this.num++;
- console.log(this);
- this.fun();
- }
- }
复制代码 箭头函数写法不推荐:- methods: {
- add2: () => {
- console.log(this);
- }
- }
复制代码 原文说明,箭头函数没有自己的 this,它的 this 来自定义时的上下文;由于对象不能构成单独作用域,父级作用域是全局作用域,所以这里通常指向 window,无法正确访问 data 中的数据。这个因果链是 Vue2 methods 中不宜使用箭头函数定义事件回调的关键原因。
异步操作中的写法需要区分场景。如果异步回调内部要修改 Vue 实例状态,原文推荐使用箭头函数继承外层 this:- methods: {
- add3() {
- setTimeout(() => {
- this.num++;
- }, 2000);
- }
- }
复制代码 这里的 setTimeout 回调写成箭头函数,this 继承 add3 的 this,仍然指向 Vue 实例。可推导出的实践是:methods 入口函数用普通函数保证 this 指向 vm;定时器、请求等异步回调内部用箭头函数保持外层 this。
四、methods 的挂载原理与简易实现
原文给出一个简易 MyVue,用 for...in 遍历 options.methods,把每个方法挂到实例上:- class MyVue {
- constructor(options) {
- for (const key in options.methods) {
- this[key] = options.methods[key];
- }
- }
- }
- const vm = new MyVue({
- data() {
- return {
- msg: 'hello vue!'
- };
- },
- methods: {
- fun1() { console.log('fun1'); },
- fun2() { console.log('fun2'); },
- fun3() { console.log('fun3'); }
- }
- });
- vm.fun1();
复制代码 这个简易实现说明了 methods 方法能被实例直接调用的基本思路。原文还总结 Vue 实际实现特点:methods 中的方法通过数据代理挂载到 Vue 实例;方法中的 this 被自动绑定到 Vue 实例;可以通过 vm 直接访问 methods 中的方法。理解这一点后,排查事件回调不生效时,可以先检查方法是否在 methods 中、模板调用名是否匹配、是否误用了箭头函数导致 this 异常。
五、落地实践清单
1. 事件处理方法统一放在 methods 中定义,模板使用 @click、@input、@keydown 等绑定。
2. 需要在回调中访问 data 或调用其他方法时,methods 入口使用普通函数。
3. 需要自定义参数时写成 @click='fun(param1, param2, $event)',用 $event 占位事件对象。
4. 只需要事件对象时写成 @click='fun',省略小括号,让 Vue 自动传入事件对象。
5. 异步回调中推荐箭头函数,以继承外层指向 Vue 实例的 this。
6. 不要把 alert 或全局函数直接当作 Vue 实例方法使用,模板中的回调应由 methods 管理。
以上写法覆盖了 Vue2 事件绑定的主要代码实践。原文的重点可以归纳为:v-on 负责事件关联,methods 负责回调管理,$event 负责参数传递,普通函数与箭头函数的选择决定 this 是否符合预期。 |