vue2 optionApi中debounce、箭头函数的问题

78 阅读1分钟

vue2 optionApi中debounce、箭头函数的问题

问题分析

1. 普通函数 vs 箭头函数的 this​ 绑定机制

普通函数

  • 具有动态的 this​ 绑定
  • this​ 的值取决于函数被调用的方式
  • 可以通过 call​、apply​ 或 bind​ 改变 this​ 的指向

箭头函数

  • 没有自己的 this​ 绑定
  • this​ 值在定义时就已经确定,继承自外层作用域
  • 无法通过 call​、apply​ 或 bind​ 改变 this​ 的指向

2. lodash 的 debounce 函数机制

lodash​ 的 debounce​ 函数会:

  1. 返回一个新的函数
  2. 在内部使用 func.apply(thisArg, args)​ 来调用原函数
  3. 保持原函数的 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. 为什么箭头函数有问题

  1. 定义时的作用域:当箭头函数定义在 methods​ 对象中时,它的 this​ 指向的是定义时的外层作用域,而不是 Vue 组件实例
  2. debounce 无法改变 this:即使 debounce​ 函数内部尝试通过 apply​ 将 Vue 实例作为 this​ 传递给箭头函数,箭头函数的 this​ 也不会改变
  3. 结果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​ 的错误。