vue3 如何更好的写hooks函数?

关于vue3如何写hooks的问题?

export function useChangeTableHeaderWidth(list) {
  const time = Date.now()
  const getWidthClass = `getWidth${time}`
  const setWidthClass = `setWith${time}`
  function resizeStep(setWidthElement: HTMLElement) {
    const fontSize = window.getComputedStyle(setWidthElement).fontSize
    let size = parseInt(fontSize)
    const scrollwidth = setWidthElement.scrollWidth
    const clientWidth = setWidthElement.clientWidth
    if (scrollwidth > clientWidth && size > 0) {
      size -= 1
      setWidthElement.style.fontSize = size + 'px'
      requestAnimationFrame(() => {
        resizeStep(setWidthElement)
      })
    }
  }
  watch(
    list,
    () => {
      const getWidth = document.getElementsByClassName(getWidthClass)
      const setWidth = document.getElementsByClassName(setWidthClass)
      for (const index in getWidth) {
        if (Object.prototype.hasOwnProperty.call(setWidth, index)) {
          /* 获取td的宽度,设置给th */
          const getWidthElement = getWidth[index]
          const width = getWidthElement.clientWidth
          const setWidthElement = setWidth[index] as HTMLElement
          // 这样写要去eslintrc文件里关闭对jsx的语法检查
          // let setWidthElement = <HTMLElement>setWidth[index]
          setWidthElement.style.width = width + 'px'

          /* title字体大于宽度的时,让字体变小 */
          const scrollwidth = setWidthElement.scrollWidth
          const clientWidth = setWidthElement.clientWidth
          if (scrollwidth > clientWidth) {
            resizeStep(setWidthElement)
          }
        }
      }
    },
    {
      flush: 'post',
    },
  )

  return [getWidthClass, setWidthClass]
}

如何限值list是ref值,亦或者即使传入的是普通值,也可以触发watch?还有就是这个hooks中的watch在使用了hooks之后一直存在吗,还是说组件销毁了watch就自动销毁,还是需要在这个hooks在写上组件卸载时手动销毁。

我记得antfu之前有个演讲提到过在hooks里用geter函数和tovalue、toref来提升兼容性,但是我记不清了,不知道能不能使用在这里,还有个effectScope我记得写hooks的时候好像也挺有用。

阅读 1k
avatarAI BotBETA

在Vue 3中编写hooks函数时,确保它们既灵活又高效是很重要的。针对你提到的问题,我会逐一解答并提供改进建议。

如何限制list是ref值或处理普通值

Vue 3的watch函数默认期望监视的是一个响应式引用(如refreactive对象)。如果你希望watch能够处理普通值(即非响应式值),你需要将这些值包装成响应式引用。这可以通过ref函数来实现。然而,如果list已经是响应式的,直接传入即可。

你可以修改你的hook来自动处理这个问题:

import { ref, watch, EffectScope } from 'vue';

export function useChangeTableHeaderWidth(list: any) {
  // 如果list不是ref,则包装成ref
  const listRef = ref(list);

  // 后续逻辑保持不变,但使用listRef.value代替list
  // ...

  watch(
    () => listRef.value,
    // ...
  );

  // 返回时可以考虑是否公开listRef.value的更新能力
  // 这里仅作为示例,实际根据需求调整
  return [getWidthClass, setWidthClass];
}

watch的销毁

在Vue 3中,watch函数自动与组件的生命周期绑定。这意味着当组件被销毁时,由该组件创建的watch也会自动停止。因此,你不需要(也不应该)在hooks中手动销毁watch

getter函数和toRef/toRefs

虽然Antfu的演讲可能提到了使用getter函数和toRef/toRefs来提升兼容性或优化,但这些通常用于当你需要从响应式对象中提取某些属性并希望保持这些属性的响应性时。在你的场景中,如果你需要保持list的某个属性的响应性,并且这个属性是reactive对象的一部分,那么toReftoRefs可能会很有用。但在你的例子中,由于你可能是在处理整个列表的响应性,直接使用ref或确保传入的是响应式对象就足够了。

EffectScope

EffectScope是Vue 3中引入的一个API,它允许你创建一个新的响应性作用域。这在你需要隔离或管理一组副作用(如watchers、computed等)时非常有用。然而,在你的例子中,如果没有特别的需求去隔离或控制这些副作用的作用域,你可能不需要直接使用EffectScope

总结

  • 使用ref来确保传入watch的值是响应式的。
  • watch会自动与组件的生命周期绑定,无需手动销毁。
  • 根据你的具体需求考虑是否使用toRef/toRefsEffectScope

希望这些解答能帮助你更好地理解和使用Vue 3中的hooks函数!

2 个回答

明显的,watch 需要监听一个响应式的值,所以你传入的 list 必须是响应式的。你如果想要限制传入的值,可以通过给 list 添加ts类型,或者通过RefSymbol来运行时判断是否是一个 ref 。

import { RefSymbol } from "@vue/reactivity"

export function useChangeTableHeaderWidth(list: Ref<Array<Something>>) {
    if (!list[RefSymbol]) {
        throw new Error('list 不是 Ref')
    }
    
    // 为了得到类型提示,你需要使用 as const
    return [getWidthClass, setWidthClass] as const
}

watch 不需要手动销毁,组件销毁时会自动销毁。

可以看看 vueuse 的仓库学习怎么写。

新手上路,请多包涵

你可以传入的值是普通值,在hooks里通过ref来进行转换成可响应值,之后将hooks转换的值返回出去,之后就修改hooks返回的值就可以了

export function useStr(str: string) {
  const newStr = ref(str)

  watch(newStr, () => {
    console.log(newStr)
  })

  return [newStr]
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏