浅析hooks,复杂前端业务解题之道

hooks 大势所趋

2019年年初,react 在 16.8.x 版本正式具备了 hooks 能力,同年6月;尤雨溪在 vue/github-issues 里提出了关于 vue3 Component API 的提案(vue hooks的基础)。在Vue3的组合式API出现后,github的一系列基于hooks的项目也是异常火爆,比较突出的是VueUse(16K stars)和 ahooks(12K stars)。由此可以预见hooks将成为业界主流,并深受广大前端程序员喜爱。

什么是hooks?

“hooks” 直译是 “钩子”,指系统运行到某一时期时,会调用被注册到该时机的回调函数。在前端提到钩子可能会跟组件的生命周期函数联系起来,但是现在明显已经给hooks赋予了新的含义。在Vue中hooks的定义为:在 vue 组合式API里,以 “use” 作为开头的,一系列提供了组件复用、状态管理等开发能力的方法。

我们为什么需要hooks?

  • 1.hooks对比于Vue2的mixin,解决了mixin的难以追朔的方法和属性问题
    mixin例子 数据来源无法追朔
export default {  
  mixins: [ a, b, c, d, e, f, g ]
    mounted() {
    console.log(this.name) //  this.name 来自于谁?
  }
}

hooks例子 数据方法来源清晰可见

<script setup lang="ts">
const { name, setName } = useName()
</script>
- 2.解决了mixin很难在一个页面重复使用(实例化多次)
mixin实现多实例繁琐且代码不易读
// 动态生成mixin
function genNameMixin(key, funcKey) {
  return {
    data() {
      return {
        [key]: genRandomName()
      }
    },
    methods: {
      [funcKey]: function(v) {
        this.[key] = v
      } 
    }
  }
}

export default {
  mixins: [
    genNameMixin('firstName', 'setFirstName'),
    genNameMixin('lastName', 'setLastName'),
  ]
}

hooks 实现多实例 简单明了

<script setup lang="ts">
import useName from './useName'

const [name,setName] = useName()
const [firstName,setFirstName] = useName()
const [secondName,setSecondName] = useName()
</script>
  • 3.hooks+组合式API优雅地实现代码组织
    在这里插入图片描述

假设把左图中分散在各处的data、mounted、methods,通过hooks按照业务组合在一起(如右图,也就是hooks和组合式API的完美结合),形成”高内聚低耦合“的代码结构,随之而来的则是代码复用率变高、效率提升、可阅读性变强、bug变少。

Vue3中如何使用hooks

  • 命名规范和指导思想
    • 约定1: 一般任何以 「use」 开头并紧跟着一个大写字母的函数就是一个 Hook
    • 约定2:一般只在最顶层使用 Hook,而不要在循环,条件或嵌套函数中调用 Hook
  • 下面是一个简单的hooks例子,下面将用这个例子进行延展
    定义了一个响应式数据name和修改name的函数setName
import { ref } from 'vue'

export default function useName() {
  const name = ref('张三')
  const setName = (val: string) => {
    name.value = val
  }
  return { name, setName }
}
  • 疑问1:hooks函数返回数组行不行?
import { ref } from 'vue'

export default function useName() {
  const name = ref('张三')
  const setName = (val: string) => {
    name.value = val
  }
  return [ name, setName ] // 返回数组
}

答:hooks返回数组当然可以,要分情况,如果hooks在一个页面内需要重复使用(也可以理解为实例化多次)则返回数组更为合适,因为我们更灵活的使用别名,demo如下:

// 使用useName
import useName from './useName'

const [name,setName] = useName()
const [firstName,setFirstName] = useName()
const [secondName,setSecondName] = useName()
反之,返回对象的hooks多实例情况如下,当然除了写法更加麻烦没其他的区别。
// 使用useName
import useName from './useName'

const {name,setName} = useName()
const {name:firstName,setName:setFirstName} = useName()
const {name:secondName,setName:setSecondName} = useName()
  • 疑问2:hooks 跟普通object写法有何区别?如下

用对象模拟hooks函数

import { ref } from 'vue'

export const useName = {
  name: ref('张三'),
  setName: (val: string) => {
    useObj.name.value = val
  }
}

答:粗略看区别不大,都能定义响应式数据,也都返回对象{name,setName}。但本质上是有区别的,该写法无法实现多实例需求,也就是说它是单例的,两个组件同时引用name数据,一个修改了则全部修改(这是区别,不是弊端,有数据/状态共享需求可以使用该方法)。

  • 疑问3: hooks是否可以使用全部组合式API?比如onUnmounted
    答:这个官方案例已经给答案了,全部组合式API都可以使用包括onUnmounted,在组件销毁时也会触发
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const x = ref(0)
const y = ref(0)

function update(event) {
  x.value = event.pageX
  y.value = event.pageY
}

onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>
  • 疑问4:hooks里写业务行不行?即使它不被复用,单纯用它解耦
    答:这个问题仁者见仁智者见智,作者认为使用hooks不用有太多思想包袱,如果需求是高复用工具函数,封装成hooks自然无可厚非,但是单纯用它处理业务闭环或处理业务逻辑拆分也是没问题的,而且这也是hooks的主要用途没有之一,它是彻底告别”屎山“业务代码的利器。

  • 疑问5:用hooks做数据/状态共享是否可行?
    答:肯定是不行的,上面问题提到hooks在调用时会生成新的实例,调用hooks返回的状态/数据内存地址是不同的,因此无法做到单例和数据共享。

最后;如果您有更多关于hooks的疑问,欢迎留言交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值