首页>>前端>>Vue->Vue3开发模式探索:基于ReturnType的多hook间返回值和参数类型实现“类型收敛”

Vue3开发模式探索:基于ReturnType的多hook间返回值和参数类型实现“类型收敛”

时间:2023-11-30 本站 点击:1

一、问题背景

自从开始使用Vue3做项目开发,把业务代码进行逻辑分块,打包成一个业务**hook** 函数就变成了我们在业务开发中的标准做法——关注点分离,这种对同一业务逻辑进行打包分块的模式的好处自然无需多说。不过在开发了一段时间之后,我们发现这种模式在处理不同业务hook 函数之间的返回值类型和入参类型的匹配的时候存在一些问题。

我们先看一段官方对组合式API做介绍时的演示代码:

const { user } = toRefs(props)const { repositories, getUserRepositories } = useUserRepositories(user)const {  searchQuery,  repositoriesMatchingSearchQuery} = useRepositoryNameSearch(repositories)const {  filters,  updateFilters,  filteredRepositories} = useRepositoryFilters(repositoriesMatchingSearchQuery)

我们可以发现:

useUserRepositories 函数返回值包括repositories

useRepositoryNameSearch 又把repositories 作为参数依赖传递了进去,同时返回了repositoriesMatchingSearchQuery

useRepositoryFilters 依赖repositoriesMatchingSearchQuery

官方演示代码是使用js演示的,使用ts的话,我们就需要补充参数的类型,一般我们会写出如下所示的代码:

// useUserRepositories.tsexport default function useUserRepositories(user: User) {  // 官方示例没有写repositories类型,我们假设这里是个字符串数组  const repositories = ref<string[]>([])  ...  return {    repositories,    ...  }}

得益于ts的自动类型推断,我们不需要显式声明repositories 的类型,ts可以推断出类型为Ref<string[]>

useRepositoryNameSearch 依赖了repositories ,代码如下所示:

// useRepositoryNameSearch.tsexport default function useRepositoryNameSearch(  repositories: Ref<string[]>) {  ...}

我们手动声明了repositories 的类型,这个类型必须和**useUserRepositories 中的repositories** 保持一致。

在写useRepositoryFilters.ts 的代码的时候也是类似的方式。

乍一看上面的代码没什么问题啊,很合理,大家似乎都是这么写的。但其实并不是这样,如果大家在使用Vue3+组合式API开发一段时间之后,就会自然而然的发现一些问题:类似于上述代码中的**repositories 变量,其实是和业务绑定很紧密的变量,useUserRepositories和useRepositoryNameSearch 也是和业务绑定紧密的hook函数,所以在对repositories** 类型的处理上应该是收敛的而不是发散的

二、类型收敛和发散

如何理解类型的收敛和发散呢?

repositories 为例,如果useUserRepositories 中的repositories 类型被修改了,那么如果我们需要手动修改**useRepositoryNameSearch 中的参数repositories** 的类型来保持一致,那么我们就认为关于repositories 的类型是分散的。而如果我们不需要手动修改,它们可以天然保持一致,那么我们就认为类型是收敛的。

类型收敛的写法在代码上看起来长如下这样:

// useRepositoryNameSearch.tsexport default function useRepositoryNameSearch(  repositories: RepositoriesType) {  ...}

可能有的同学会说,这简单,我们单独给repositories 声明一个RepositoriesType 或者是给useUserRepositories 单独声明一个UseUserRepositoriesReturnType ,类似于如下这样:

type RepositoriesType = Ref<string[]>type UseUserRepositoriesReturnType = {    repositories: Ref<string[]>}

这些做法都是符合类型收敛的,都是正确的,但是似乎不够简洁。

在函数返回值较多的时候,针对每个返回值声明类型或者是声名一个大类型包含了函数所有返回值,都是有开发成本和维护成本,重点是这些工作本身ts已经帮我们做了,那就是ts类型推断,得益于此,我们可以写出很干净简洁的代码,而无需做过多冗余的声明:

三、基于ReturnType的类型收敛

在前一部分我们已经知道ts类型推断可以自动推断出函数的返回值类型,那么我们就只需要获取到这个推断出的类型就可以了,这一点基于ts工具类型ReturnType 就可以简单实现:

type UseUserRepositoriesReturnType = ReturnType<typeof useUserRepositories>

这时候,在useRepositoryNameSearch 中,我们就可以不用在重新定义repositories 的类型:

import {UseUserRepositoriesReturnType} from "useUserRepositories.ts"export default function useRepositoryNameSearch(  repositories: UseUserRepositoriesReturnType['repositories']) {  ...}

UseUserRepositoriesReturnType['repositories'] 中的字符串'repositories' 是可以获得代码自动补全支持的。不需要手动输入。

四、总结

首先,没有什么模式是万能的,只有最适合的才是最好的。关于这种方式有的同学可能会有疑惑,“这么写是不是会把两个函数的返回值类型和参数类型耦合在一起了?”

确实是这样的。如果我们写的是一个公共函数,这个函数没有深度耦合的业务场景,就是为了复用,那么类型保持独立,不与其他的类型存在耦合是没问题的,这也是一种类型收敛。

所以我在本文中阐述的开发模式,更适合与业务深度耦合的业务hook函数。因为这种模式确实在我们的业务开发中比较适合。

这篇文章是我个人对业务开发中如何优化代码的一些简单思考,欢迎大家留言讨论~

原文:https://juejin.cn/post/7096367390400708644


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Vue/3781.html