作者|Mateusz Rybczonek
译者|王强
编辑|王文婧
虽然 Vue3.0 尚未发布,但前段时间,Vue 发布了关于 Composition API 的官方插件。本文作者通过实际试用,对 Composition API 与当前的 Options API 作出了一番对比,并对 Composition API 的特征与用途进行了通俗易懂的总结。一起来看看!
我最近得到了机会,在一个真实的项目中试用了 Vue 中全新的 Composition API,进而对它可能的用途以及未来的用法探索了一番。
现在,当我们创建一个新组件时使用的是 Options API。使用这个 API 时,我们必须通过选项将组件的代码分离开来,这意味着我们需要将所有响应性数据放在一个地方,所有计算的属性放在一个位置,所有方法也都放在一个位置,依此类推。
这个 API 在处理较小的组件时比较易读和顺手,而当组件变得更加复杂,需要处理多种功能时,它用起来就很痛苦了。一般来说,与一个特定功能相关的逻辑会包含一些响应性数据、一些计算属性,还有一种或一些方法。有时还会用到组件生命周期 hooks。于是在处理单个逻辑问题时,需要不断在代码中的不同选项之间来回切换。
使用 Vue 时,可能遇到的另一个问题是设法提取可被多个组件复用的通用逻辑。Vue 已经提供了一些方案可供选择,但它们都有各自的缺点(例如 mixins 和作用域插槽)。而新的 Composition API 带来了一种创建组件、分离代码和提取可复用代码段的全新方式。
首先来看组件内的代码构成。
代码构成
假设你有一个核心组件,为整个 Vue 应用设置了一些内容(就像 Nuxt 中的布局)。它负责处理以下内容:
设定区域;
检查用户是否处于登录状态,如果没有,则将其重定向;
防止用户重新加载应用程序太多次数;
跟踪用户活动,并在用户静默一段时间后做出反应;
使用 EventBus 监听事件(或窗口对象事件)。
这些只是这个组件可以做的事情的一些例子。你可能会设想出一个更复杂的组件,不过这里的这些已经足够本文举例说明了。为了便于阅读,我只用了 props 的名称,而没有实际实现。
下面是使用 Options API 时组件的样子:
如你所见,每个选项都包含所有功能的其中一部分。它们之间没有明确的分隔,这使代码很难阅读。如果代码并不是你写的,你还是第一次看到它,那么读起来会更费劲,很难分清具体哪种功能使用的是哪种方法。
我们再来看一下,但这次用注释把逻辑上的关注点标识出来。这些关注点包括:
Activity Tracker(活动追踪);
Reload Blocker(阻止重新加载);
Authentication check(登录状态检查);
Locale(区域选项);
Event Bus Registration(EventBus 注册)。
由此可见,解开这团乱麻有多复杂。
现在假设你需要更改一种功能(例如活动追踪逻辑)。你不仅需要知道有哪些元素与该逻辑相关,而且就算你知道了,也需要在不同的组件选项之间跳来跳去。
下面我们使用 Composition API,通过逻辑关注点来分离代码。为此,我们为每个与特定功能相关的逻辑创建一个函数。这就是我们所说的composition 函数。
// Activity tracking logic
functionuseActivityTracker(){
constuserActivityTimeout = ref(null)
constlastUserActivityAt = ref(null)
functionactivateActivityTracker(){...}
functiondeactivateActivityTracker(){...}
functionresetActivityTimeout(){...}
functionuserActivityThrottler(){...}
onBeforeMount(()=>{
activateActivityTracker()
resetActivityTimeout()
})
onUnmounted(()=>{
deactivateActivityTracker()
clearTimeout(userActivityTimeout.value)
})
}
// Reload blocking logic
functionuseReloadBlocker(context){
constreloadCount = ref(null)
functionblockReload(){...}
functionsetReloadCount(){...}
onMounted(()=>{
setReloadCount()
blockReload()
})
}
// Locale logic
functionuseLocale(context){
asyncfunctionloadLocaleAsync(selectedLocale){...}
functionsetI18nLocale(locale){...}
watch(()=>{
constlocale = ...
loadLocaleAsync(locale)
})
// No need for a 'created' hook, all logic that runs in setup function is placed between beforeCreate and created hooks
constinitialLocale = localStorage.getItem('locale')
loadLocaleAsync(initialLocale)
}
如你所见,我们可以声明响应性数据(ref/reactive)、计算的 props、方法(纯函数)、观察者(watch)和生命周期 hooks(onMounted/onUnmount)。基本上你平时在组件中使用的所有内容都能声明。
关于保存代码的位置,我们有两个选择。我们可以将其保留在组件中,或提取到单独的文件中。由于 Composition API 尚未正式发布,因此还没有关于如何使用它的最佳实践或规则。我的看法是,如果逻辑与特定组件紧密耦合(即不会在其他任何地方复用),并且逻辑离开了组件就无法生存,我建议将其保留在组件中。另一方面,如果是可能会被复用的一般性功能,则建议将其提取到单独的文件中。但如果我们要将其保存在单独的文件中,则需要从文件中导出函数并将其导入到组件中。
这是使用新创建的 composition 函数后,我们组件的样子:
这里每个逻辑关注点都有了一个函数。如果要使用某一个关注点,则需要在新的 setup 函数中调用相关的 composition 函数。
再设想一下,你需要对活动跟踪逻辑作一些更改。与该功能相关的所有内容都放在 useActivityTracker 函数中。现在你就能立刻找出并跳转到正确的位置,查看所有相关的代码段了。非常漂亮!
提取可复用的代码段
在我们的例子中,事件总线侦听器注册(Event Bus listener registrations)看来是一段代码,如果有组件需要侦听事件总线上的事件,我们就可以用这段代码来实现。
如前所述,我们可以将与特定功能相关的逻辑保存在单独的文件中。下面我们将事件总线侦听器设置转移到一个单独的文件中。
要在组件中使用它,我们需要导出函数(命名或默认),并将其导入组件中。
完事了!现在我们能在任何组件中使用它了。
小 结
关于 Composition API 的讨论还在进行中。这篇文章无意在讨论中站队,更关心的是新 API 可能的用途,以及它能在哪些情况下带来附加价值。
我认为在现实案例中理解概念总是比较容易的,就像上面这个例子一样。用例越多,使用新 API 的次数越多,我们也就能发现更多的模式。这篇文章只涉及了一些基本的模式,供抛砖引玉。
我们再来看一遍上面这些用例,看看 Composition API 的用途有哪些:
1. 无需与任何特定组件紧密耦合也能独立运行的一般性功能
与一个特定功能相关的所有逻辑都放在一个文件中;
将其保存在 @/composables/*.js,并将其导入组件中;
示例:活动跟踪、阻止重新加载和区域设置。
2. 可在多个组件中使用的可复用功能
与一个特定功能相关的所有逻辑都放在一个文件中;
将其保存在 @/composables/*.js,并将其导入组件中;
示例:事件总线侦听器注册、窗口事件注册、通用动画逻辑、通用库的使用。
3. 组件内的代码组织
与一个特定功能相关的所有逻辑都放在一个函数中;
将代码保留在组件内的 composition 函数中;
与同一逻辑关注点相关的代码位于同一位置。也就是说,无需在数据、计算属性、方法、生命周期 hooks 等内容之间来回跳转)。
记住:这些都尚在开发中!
Vue Composition API 目前尚处于开发阶段,未来还可能出现更改。上面示例中提到的任何内容、语法和用例都可能出现变化。这个 API 计划将随 Vue 3.0 一起推出。另外,你可以在 view-use-web 上查看一组 composition 函数的信息,这些函数预计会包含在 Vue 3 中,但也能用在 Vue 2 中的 Composition API 上。
如果你想尝试新的 API,可以使用 @vue/composition 库。
领取专属 10元无门槛券
私享最新 技术干货