vue2 optionApi中debounce、箭头函数的问题
问题分析
1. 普通函数 vs 箭头函数的 this 绑定机制
普通函数:
- 具有动态的
this 绑定 -
this 的值取决于函数被调用的方式 - 可以通过
call、apply 或bind 改变this 的指向
箭头函数:
- 没有自己的
this 绑定 -
this 值在定义时就已经确定,继承自外层作用域 - 无法通过
call、apply 或bind 改变this 的指向
2. lodash 的 debounce 函数机制
lodash 的 debounce 函数会:
- 返回一个新的函数
- 在内部使用
func.apply(thisArg, args) 来调用原函数 - 保持原函数的
this 上下文
3. 在 Vue methods 中的实际情况
// 当前代码(正确)
methods: {
handleResize: debounce(function() {
const newWidth = window.innerWidth;
this.$store.commit('setIsPC', newWidth > 780); // this 指向 Vue 实例
}, 100),
}
// 如果使用箭头函数(错误)
methods: {
handleResize: debounce(() => {
const newWidth = window.innerWidth;
this.$store.commit('setIsPC', newWidth > 780); // this 不指向 Vue 实例
}, 100),
}
4. 为什么箭头函数有问题
- 定义时的作用域:当箭头函数定义在
methods 对象中时,它的this 指向的是定义时的外层作用域,而不是 Vue 组件实例 - debounce 无法改变 this:即使
debounce 函数内部尝试通过apply 将 Vue 实例作为this 传递给箭头函数,箭头函数的this 也不会改变 - 结果:
this.$store 会是undefined,导致运行时错误
5. 解决方案
保持使用普通函数,或者显式绑定 this:
// 方案1:使用普通函数(推荐)
handleResize: debounce(function() {
const newWidth = window.innerWidth;
this.$store.commit('setIsPC', newWidth > 780);
}, 100),
// 方案2:如果一定要用箭头函数,需要在外层获取 this
handleResize() {
const vm = this;
return debounce(() => {
const newWidth = window.innerWidth;
vm.$store.commit('setIsPC', newWidth > 780);
}, 100);
}
总结
在这个例子中,使用普通函数可以让 debounce 正确地保持 Vue 组件实例的 this 上下文,而箭头函数由于其词法作用域的特性,无法被 debounce 函数动态改变 this 指向,从而导致 this.$store 为 undefined 的错误。