发布于 2026-01-06 1 阅读
0

考虑使用 Vue Composition API 来提升代码质量:1. 不再使用 this instance;2. 更好的代码拆分管理;3. 函数复用;4. 对 TypeScript 接口的更多控制。结论

考虑使用 Vue Composition API 来提高代码质量

1. 不再有this实例

2. 更好的代码分割管理

3. 函数重用性

4. 对 TypeScript 接口的更多控制

结论

嘿!

自从 Vue 3 beta 版三月份发布以来,我一直在使用 Vue Composition API 进行开发和原型设计。如果您正计划使用新的 Vue 3,或者从 Vue 2 迁移到 Vue 3,我想分享一些我使用过程中积累的宝贵经验,供您参考。让我们开始吧!

注意:代码示例基于新的 Vue 3 规范。

1. 不再有this实例

作为 JavaScript 开发人员,由于 JavaScript 中对象或类实例继承的常见特性,我们可能需要在很多情况下处理this变量。其中一个常见的问题包括:

“在大多数情况下,此值由函数的调用方式(运行时绑定)决定。它不能在执行期间通过赋值来设置,并且每次调用函数时其值可能不同。 ” - MDN

在使用基于对象的属性编写 Vue 组件时,您可能会遇到类似的情况,因为this实例与 Vue 继承对象属性和根原型这一概念紧密相关。以下是一个名为 `<component>` 的组件示例my-counter,该组件应在点击“添加”按钮或按下+键盘上的相应键时增加计数值。

<template>
  <div>Count: {{ count }}
    <button @click="incrementCount">Add</button>
  </div>
</template>
<script>
export default {
  name: 'my-counter',
  data () {
    return {
      count: 0
    }
  },
  mounted () {
    // register keyboard event to listen to the `+` key press
    document.addEventListener('keydown', function(e) {
      if (e.keyCode === 187) { // 187 is keyCode for `+`
        this.incrementCount()
      }
    })
  },
  methods: {
    incrementCount () {
      this.count += 1
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

它看起来简洁明了。请注意this,方法中的 `in` 包含了我们之前定义的.countdata。但除此之外,this它还包含更多信息,例如 Vue 根实例、已安装的插件(vuex、router 等)、$attrs插槽等等。

你发现上面的代码有个bug了吗?如果发现了,那真是好眼力!按下+键盘上的某个键时会出现错误,提示:

Uncaught TypeError: this.incrementCount is not a function
Enter fullscreen mode Exit fullscreen mode

这是因为事件监听器的回调函数绑定到的是实例本身document,而不是Vue组件。这个问题可以通过修改函数方法轻松解决arrow based function,但初学者可能一开始并没有意识到这一点,他们需要理解 JavaScript 的继承概念才能适应这种情况。

好了,抱歉帖子有点长🥔,是为了解释一些基本特性this,现在让我们深入了解组合 API!

在组合式 API 中,它不依赖于this实例。所有操作都在阶段内完成setup,该阶段包括创建组件的数据和方法。以下是基于上述组件的组合式 API 示例my-counter

<template>
  <div>Count: {{ count }}
    <button @click="incrementCount">Add</button>
  </div>
</template>
<script>
import { reactive, toRefs, onMounted } from 'vue'

export default {
  name: 'my-counter',
  setup () {
    const data = reactive({
      count: 0
    })

    const incrementCount = () => data.count++

    onMounted(function () {
      document.addEventListener('keydown', function(e) {
        if (e.keyCode === 187) { // 187 is keyCode for '+'
          incrementCount()
        }
      })
    })

    return {
      ...toRefs(data),
      incrementCount
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

让我们比较一下区别。以前,你依赖对象属性data来注册状态count,并methods注册递增状态的函数count。现在,methods你依赖this实例来访问状态count值。

重构到 Composition API 后,所有功能都被封装起来,setup用于初始化数据、创建用于修改计数的函数以及附加键盘事件监听器。this值方面不再有任何怪癖,因此无论是普通按键还是方向键都不会再有问题了!

2. 更好的代码分割管理

通过上面的 Composition API 示例,我们可以看到,现在我们不必遵循 Vue 的约定将组件功能编写到单独的属性中(lifecycle hooks, data, methods, computed, watch),因为所有内容都可以组合成一个函数setup

这样一来,我们就有机会拆分代码,以便更好地组织代码,尤其是在组件功能复杂的情况下。我们可以将所有功能都写在同一个框架下setup,也可以创建一个 JS 文件,将特定功能限定在其他文件中。

我们以组件中的例子为例my-counter。如果我们想将附加键盘事件的功能拆分出来,单独处理该怎么做呢?

// keyboard-event.js
import { onMounted } from 'vue'

export function usePlusKey (callbackFn) {
  onMounted(function () {
    document.addEventListener('keydown', function(e) {
      if (e.keyCode === 187) { // 187 is keyCode for '+'
        callbackFn()
      }
    })
  })
}
Enter fullscreen mode Exit fullscreen mode

现在,我们可以导入并使用此函数setup

import { reactive, toRefs } from 'vue'
import { usePlusKey } from './keyboard-event' 

export default {
  name: 'my-counter',
  setup () {
    const data = reactive({
      count: 0
    })

    const incrementCount = () => data.count++

    usePlusKey(incrementCount)

    return {
      ...toRefs(data),
      incrementCount
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

你可能会质疑是否应该拆分上面的键盘监听器函数,但我希望你能明白,管理代码是你自己的责任,而 Composition API 为你提供了一种更便捷的方式来处理它。你在上面看到的另一个优点是,组件的生命周期钩子可以单独定义!

如果需要在挂载时处理多个场景,现在可以将它们拆分处理。例如:

// my-component.vue
mounted () {
  this.initPayment()
  this.initTracking()
},
methods: {
  initPayment () { /* init payment */ },
  initTracking () { /* init tracking */ }
}
Enter fullscreen mode Exit fullscreen mode

使用 Composition API:

// my-component/payment.js
export function initPayment () {
  onMounted(() => { /* init payment */ })
}

// my-component/tracking.js
export function initTracking () {
  onMounted(() => { /* init tracking */ })
}

// my-component.vue
import { initPayment } from './payment'
import { initTracking } from './tracking' 

setup () {
  initPayment()
  initTracking()
}
Enter fullscreen mode Exit fullscreen mode

3. 函数重用性

通过上面的例子,我们可以看到该函数不仅适用于一个组件,还可以用于其他组件!

代码重用性的概念与mixin类似。然而,mixin 也存在一个缺点,这里对此进行了解释。简而言之,命名冲突和隐式依赖是“隐藏的 bug”,如果您使用不当,可能会遇到麻烦。

使用 Composition API 时,需要关注这两个方面。 已消失这种情况发生的可能性较小,因为组合 API 函数需要明确定义它所需的参数值以及返回值的变量名。

让我们来看一个计数器功能的 mixin 示例:

// mixin/counter.js
const mixinCounter = {
  data () {
    return {
      counter: 0
    }
  },
  methods: {
    increment () {
      this.counter++
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

使用此 mixin 时,必须注意它可能会覆盖其安装的组件中已有的counter数据和increment方法。这就是所谓的“隐式依赖”。

如果我们将其转换为 Composition API:

// compose/counter.js
import { ref } from 'vue'
export function useCounter () {
  const counter = ref(0)
  const increment = () => counter.value++
  return {
    counter,
    increment
  }
}
Enter fullscreen mode Exit fullscreen mode

使用此函数,它会显式返回 ` counterand` 值,increment并让组件setup决定如何处理它。如果名称counter/increment已被使用或需要多次使用,我们仍然可以通过重命名变量来解决这个问题,如下所示:

// use default counter and increment name
const { counter, increment } = useCounter()

// since counter and increment already exist,
// rename it to countQty and incrementQty
const { counter: countQty, increment: incrementQty } = useCounter()
Enter fullscreen mode Exit fullscreen mode

酷!或许这里需要考虑的一点是,你需要一些额外的时间来仔细考虑一下变量的新名称😅。

4. 对 TypeScript 接口的更多控制

你是否使用 TypeScript 正确地为组件接口编写类型?如果是,那就太好了!

根据官方文档,Vue 提供了基本的 TypeScript 支持Vue.extend,或者可以使用vue-class-component将 Vue 组件编写为类,利用this实例正确地对数据和方法进行类型化。

如果想要摆脱this怪癖,同时又能拥有强大的类型接口,那么组合式 API 就是一个不错的选择。

首先,这是一个纯函数,它接受输入参数来替代访问组件和上下文的setup需求,,thispropsattrsslotsemit

然后,所有你在 `<div>` 标签中编写的数据和函数setup都由你来输入 😍!你可以编写和输入你的代码,而无需遵循 Vue 定义 `<div>` datamethods`<div>`、refs`<div>`computed和`<div>` 的方式watch

以下是一个类型化的 Vue 组件示例:

// we use Vue.extend in vue v2.x
export default Vue.extend({
  data () {
    return {
      count: 0
    }
  },
  computed: {
    multiplyCount () {
      return this.count * 2
    }
  },
  methods: {
    increment () {
      this.count++
    }
  },
  watch: {
    count (val) { // `val` type is `any` :(
      console.log(val) 
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们依赖于Vue.extend组件接口自动进行类型判断。this.count计算属性multiplyCount和方法increment会从组件接口获取正确的类型data,但监听器count不会被赋予类型😕。

让我们看看它在 Composition API 中是如何编写的:

// in vue 3.x, we use defineComponent
export default defineComponent({
  setup () {
    const count = ref(0) // typed to number
    const multiplyCount = computed(() => count.value * 2 )
    const increment = () => count.value++
    watch(count, val => console.log(val)) // `val` is typed to number
    return {
      count,
      multiplyCount,
      increment
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

这里的输入方式更加明确和可预测。你还可以根据需要自定义输入方式,这意味着你可以完全掌控界面!

结论

以上就是我关于如何使用 Vue Composition API 的所有见解!

我相信 Composition API 还有很大的潜力,所以请分享一下您的使用体验或想法!任何改进建议也欢迎提出 😍

我还要强调一点,组合式 API 并不是万能的,如果您没有看到使用组合式 API 的好处,或者您的组件非常简单,则不必将组件重构为组合式 API 。

谢谢,祝您今天过得愉快!

文章来源:https://dev.to/chenxeed/consider-vue-composition-api-to-improve-the-code-quality-31ne