文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

在vue3中如何编写一个标准的hooks?

2024-11-29 20:06

关注

hooks的定义

其实,事实上官方并未管这种方式叫做hooks,而似乎更应该叫做compositions更加确切些,更加符合vue3的设计初衷。由于react的hooks设计理念在前,而vue3的组合式使用也像一个hook钩子挂载vue框架的生命周期中,对此习惯性地称作hooks。

对于onMounted、onUnMounted等响应式API都必须在setup阶段进行同步调用。

图片

要理解 Vue 3 中的 Hooks,需要明白它的本质是一个函数,这个函数可以包含与组件相关的状态和副作用操作。

记住:hooks就是特殊的函数,可以在vue组件外部使用,可以访问vue的响应式系统。

vue3中hooks和react的区别

vue3的compositions和react的hooks还是有所区别的,对此官方还特别写了两者的比较,原文如下:

图片

大抵意思如下,Vue Composition API 与 React Hooks 都具有逻辑组合能力,但存在一些重要差异。

React Hooks 的问题:

Vue Composition API 的优势:

hooks工具库vueuse和vue-hooks-plus

对于常用的hooks方法可以单独抽取进行发包成hooks工具。在业务开发中常用的vue hooks方法库有:vueuse和vue-hooks-plus。那么,咱们看看这两个库对于useCounter的封装是什么样的。

vueuse:

// eslint-disable-next-line no-restricted-imports
import { ref, unref } from 'vue-demi'

import type { MaybeRef } from 'vue-demi'

export interface UseCounterOptions {
  min?: number
  max?: number
}


export function useCounter(initialValue: MaybeRef = 0, options: UseCounterOptions = {}) {
  let _initialValue = unref(initialValue)
  const count = ref(initialValue)

  const {
    max = Number.POSITIVE_INFINITY,
    min = Number.NEGATIVE_INFINITY,
  } = options

  const inc = (delta = 1) => count.value = Math.min(max, count.value + delta)
  const dec = (delta = 1) => count.value = Math.max(min, count.value - delta)
  const get = () => count.value
  const set = (val: number) => (count.value = Math.max(min, Math.min(max, val)))
  const reset = (val = _initialValue) => {
    _initialValue = val
    return set(val)
  }

  return { count, inc, dec, get, set, reset }
}

vue-hooks-plus:

import { Ref, readonly, ref } from 'vue'
import { isNumber } from '../utils' // export const isNumber = (value: unknown): value is number => typeof value === 'number'

export interface UseCounterOptions {
  
  min?: number

  
  max?: number
}

export interface UseCounterActions {
  
  inc: (delta?: number) => void

  
  dec: (delta?: number) => void

  
  set: (value: number | ((c: number) => number)) => void

  
  reset: () => void
}

export type ValueParam = number | ((c: number) => number)

function getTargetValue(val: number, options: UseCounterOptions = {}) {
  const { min, max } = options
  let target = val
  if (isNumber(max)) {
    target = Math.min(max, target)
  }
  if (isNumber(min)) {
    target = Math.max(min, target)
  }
  return target
}

function useCounter(
  initialValue = 0,
  options: UseCounterOptions = {},
): [Ref, UseCounterActions] {
  const { min, max } = options

  const current = ref(
    getTargetValue(initialValue, {
      min,
      max,
    }),
  )

  const setValue = (value: ValueParam) => {
    const target = isNumber(value) ? value : value(current.value)
    current.value = getTargetValue(target, {
      max,
      min,
    })
    return current.value
  }

  const inc = (delta = 1) => {
    setValue(c => c + delta)
  }

  const dec = (delta = 1) => {
    setValue(c => c - delta)
  }

  const set = (value: ValueParam) => {
    setValue(value)
  }

  const reset = () => {
    setValue(initialValue)
  }

  return [
    readonly(current),
    {
      inc,
      dec,
      set,
      reset,
    },
  ]
}

export default useCounter

两段代码都在代码实现上都遵守了上面的hook军规,实现了相似的功能,即创建一个可复用的计数器模块,具有增加、减少、设置特定值和重置等操作,并且都可以配置最小和最大计数范围。

差异点

  1. 代码细节:
  1. 返回值处理:

那么什么场景下需要抽取hooks呢?

在以下几种情况下,通常需要抽取 Hooks 方法:

1.逻辑复用当多个组件中存在相同或相似的逻辑时,抽取为 Hooks 可以提高代码的复用性。例如,在多个不同的页面组件中都需要进行数据获取和状态管理,如从服务器获取用户信息并显示加载状态、错误状态等。可以将这些逻辑抽取为一个useFetchUser的 Hooks 方法,这样不同的组件都可以调用这个方法来获取用户信息,避免了重复编写相同的代码。

2.复杂逻辑的封装如果某个组件中有比较复杂的业务逻辑,将其抽取为 Hooks 可以使组件的代码更加清晰和易于维护。比如,一个表单组件中包含了表单验证、数据提交、错误处理等复杂逻辑。可以将这些逻辑分别抽取为useFormValidation、useSubmitForm、useFormErrorHandling等 Hooks 方法,然后在表单组件中组合使用这些 Hooks,使得表单组件的主要逻辑更加专注于用户界面的呈现,而复杂的业务逻辑被封装在 Hooks 中。

3.与特定功能相关的逻辑当有一些特定的功能需要在多个组件中使用时,可以抽取为 Hooks。例如,实现一个主题切换功能,需要管理当前主题状态、切换主题的方法以及保存主题设置到本地存储等逻辑。可以将这些逻辑抽取为useTheme Hooks 方法,方便在不同的组件中切换主题和获取当前主题状态。

4.提高测试性如果某些逻辑在组件中难以进行单元测试,可以将其抽取为 Hooks 以提高测试性。比如,一个组件中的定时器逻辑可能与组件的生命周期紧密耦合,难以单独测试。将定时器相关的逻辑抽取为useTimer Hooks 方法后,可以更容易地对定时器的行为进行单元测试,而不依赖于组件的其他部分。

总之,抽取 Hooks 方法可以提高代码的复用性、可维护性和测试性,当遇到上述情况时,考虑抽取 Hooks 是一个很好的实践。

案例:vue-vben-admin中的usePermission

我们看看关于在业务开发中如何进行hooks抽取封装的案例,vue-vben-admin(https://github.com/vbenjs/vue-vben-admin)是个优秀的中后台管理项目,在项目中设计很复杂也很全面,很多地方都充分体现了vue3的设计思想,也能窥见作者对于vue3源码的深入。

import type { RouteRecordRaw } from 'vue-router';

import { useAppStore } from '/@/store/modules/app';
import { usePermissionStore } from '/@/store/modules/permission';
import { useUserStore } from '/@/store/modules/user';

import { useTabs } from './useTabs';

import { router, resetRouter } from '/@/router';
// import { RootRoute } from '/@/router/routes';

import projectSetting from '/@/settings/projectSetting';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { RoleEnum } from '/@/enums/roleEnum';

import { intersection } from 'lodash-es';
import { isArray } from '/@/utils/is';
import { useMultipleTabStore } from '/@/store/modules/multipleTab';

// User permissions related operations
export function usePermission() {
  const userStore = useUserStore();
  const appStore = useAppStore();
  const permissionStore = usePermissionStore();
  const { closeAll } = useTabs(router);

  
  async function togglePermissionMode() {
    appStore.setProjectConfig({
      permissionMode:
        appStore.projectConfig?.permissionMode === PermissionModeEnum.BACK
          ? PermissionModeEnum.ROUTE_MAPPING
          : PermissionModeEnum.BACK,
    });
    location.reload();
  }

  
  async function resume() {
    const tabStore = useMultipleTabStore();
    tabStore.clearCacheTabs();
    resetRouter();
    const routes = await permissionStore.buildRoutesAction();
    routes.forEach((route) => {
      router.addRoute(route as unknown as RouteRecordRaw);
    });
    permissionStore.setLastBuildMenuTime();
    closeAll();
  }

  
  function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
    // Visible by default
    if (!value) {
      return def;
    }

    const permMode = projectSetting.permissionMode;

    if ([PermissionModeEnum.ROUTE_MAPPING, PermissionModeEnum.ROLE].includes(permMode)) {
      if (!isArray(value)) {
        return userStore.getRoleList?.includes(value as RoleEnum);
      }
      return (intersection(value, userStore.getRoleList) as RoleEnum[]).length > 0;
    }

    if (PermissionModeEnum.BACK === permMode) {
      const allCodeList = permissionStore.getPermCodeList as string[];
      if (!isArray(value)) {
        return allCodeList.includes(value);
      }
      return (intersection(value, allCodeList) as string[]).length > 0;
    }
    return true;
  }

  
  async function changeRole(roles: RoleEnum | RoleEnum[]): Promise {
    if (projectSetting.permissionMode !== PermissionModeEnum.ROUTE_MAPPING) {
      throw new Error(
        'Please switch PermissionModeEnum to ROUTE_MAPPING mode in the configuration to operate!',
      );
    }

    if (!isArray(roles)) {
      roles = [roles];
    }
    userStore.setRoleList(roles);
    await resume();
  }

  
  async function refreshMenu() {
    resume();
  }

  return { changeRole, hasPermission, togglePermissionMode, refreshMenu };
}

这段代码实现了一个与权限管理相关的模块,主要用于在 Vue 应用中处理用户权限、切换权限模式、重新获取权限资源信息以及刷新菜单等操作。

主要结构和组成部分

  1. 引入依赖:
  1. 定义**usePermission**函数:
  1. 返回值:

总结

本文主要介绍了 Vue 3 中的组合式 API 及 Hooks 相关内容。首先说明了 Vue 3 组合式 API 中 Hooks 的概念、作用及与 React Hooks 的区别,指出 Vue Composition API 的优势。接着详细阐述了编写自定义 Hooks 时应避免的错误和陷阱,如状态共享、副作用处理、过度依赖外部状态等问题,并给出了自定义 Hooks 函数的示例及单元测试方法。然后对比了两个库(vueuse 和 vue-hooks-plus)对 useCounter 的封装差异。还探讨了抽取 Hooks 的场景,如逻辑复用、复杂逻辑封装等,并以 vue-vben-admin 项目中的权限管理模块为例进行分析。

参考素材:

来源:宇宙一码平川内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯