巧用vue3 hook 新方式

1,151 阅读3分钟

vue3已经出来一段时间了,随着IE的告别vue3也被广泛应用到项目中。就算如此,还是避免不了我们在使用vue3时存在一些的不足的地方,为此,我们不得不去阅读他人总结的文章来查漏补缺。

文章不会很长,大部分会用用例进行描述,耐心看到最后相信你会受益颇多,甚至会触动你重构自己代码的决心。

如果你只是认为本文是怎么教你去设计你的组件,怎么编排你的组件,怎么去优化你的组件,我想跟你说NO!NO!NO,不信请往下看。

需求

本文将通过一个articles业务模块(articles的增删改查,看下图),一步步的去引导怎么去优化自己的代码。

1669998649789.jpg

代码

假如这是一个articles/index.vue(简称index.vue),具体业务实现逻辑如下:

<script setup>
  import ArticleTable from './components/ArticleTable.vue'
  import ArticleFormModal from './components/ArticleTable.vue'
  import { onMounted, ref } from 'vue'

  /* state */
  const visible = ref(false)
  const data = ref([])
  const initialState = ref()

  /* 钩子函数 */
  onMounted(() => {
    getArticles()
  })

  /* 交互method */
  const openEditFormModal = (init) => {
    initialState.value = init
    visible.value = true
  }
  const openCreateFormModal = () => {
    visible.value = true
  }

  // 接口处理 method
  const getArticles = () => {}
  const onUpdateArticle = () => {}
  const onCreateArticle = () => {}
  const onDeleteArticle = () => {}
</script>

<template>
  <page-layout>
    <page-header>
      <a-button type="primary" @click="openCreateFormModal">添加</a-button>
    </page-header>
    <page-content>
      <article-table @delete="onDeleteArticle" @edit="openEditFormModal" />
      <article-form-modal
        :visible="visible"
        :initial-state="initialState"
        @create="onUpdateArticle"
        @update="onCreateArticle"
      />
    </page-content>
  </page-layout>
</template>

相信一部人会这么写到这里就停了。如果这只是上面那么一个简单的功能,这么写也没有多大问题,阅读起来还是很ok,可问题就在我们的业务偏偏不会这么简单,我们的产品经理日思夜想的怎么给我们添加杂七杂八的需求,我们的index.vue文件将会越来越大,越来越杂,如果不想被队友骂(相信接手别人代码的人总会那么吐槽几句),我们不得不的进一步优化。

Hook 登场

articles/hook.js

import { onMounted, ref } from 'vue'
export const useArticle = () => 
  /* state */
  const visible = ref(false)
  const data = ref([])
  const initialState = ref()

  /* 钩子函数 */
  onMounted(() => {
    getArticles()
  })

  /* 交互method */
  const openEditFormModal = (init) => {
    initialState.value = init
    visible.value = true
  }
  const openCreateFormModal = () => {
    visible.value = true
  }

  // 接口处理 method
  const getArticles = () => {}
  const onUpdateArticle = () => {}
  const onCreateArticle = () => {}
  const onDeleteArticle = () => {}

  return {
      visible,
      data,
      initialState,
      openEditFormModal,
      openCreateFormModal
      onUpdateArticle,
      onCreateArticle,
      onDeleteArticle
  }
}

export default article

articles/index.vue

<script setup>
  import ArticleTable from './components/ArticleTable.vue'
  import ArticleFormModal from './components/ArticleTable.vue'
  import { useArticle } from './hook'
   
   const {
          visible,
          data,
          initialState,
          openEditFormModal,
          openCreateFormModal
          onUpdateArticle,
          onCreateArticle,
          onDeleteArticle
      } = useArticle()
</script>

<template>
  <page-layout>
    <page-header>
      <a-button type="primary" @click="openCreateFormModal">添加</a-button>
    </page-header>
    <page-content>
      <article-table @delete="onDeleteArticle" @edit="openEditFormModal" />
      <article-form-modal
        :visible="visible"
        :initial-state="initialState"
        @create="onUpdateArticle"
        @update="onCreateArticle"
      />
    </page-content>
  </page-layout>
</template>

看起来不错,我们的index.vue文件简洁了许多,我们某个功能模块的状态值和处理逻辑都整合到了一个js文件,这阅读起来也舒服了许多。

但是我想说的是,这已经是我们优化的尽头了吗。有没有想过以下几个问题:

  1. 我们每次使用ArticleTable、ArticleFormModal类似的组件都需要填写一个复杂的vue组件,然后通过import引进来(除全局注册的组件之外)。
  2. 我们的ArticleTable、ArticleFormModal需要通过声明一堆emit来与父组件进行通信。

接下来就是我们的最终优化。

HOOK + component < useHookComponent

自从vue3 引入composition api 以及 hook 以来,我一直就在思考,我们编写hook代码仅仅只能编写一些 state(ref、reactive等)、method而已吗?我们可不可以把component也整合进去。于是就有了以下写法:

articles/index.vue

   <script setup>
  import { message } from 'ant-design-vue'
  import useArticleFormModal from './hooks/useArticleFormModal'
  import useArticleTable from './hooks/useArticleTable'

  defineOptions({
    name: 'Article',
  })

  const {
    openFormModal,
    openEditFormModal,
    closeFormModal,
    FormModal,
  } = useArticleFormModal()

  const { 
      onCreate, 
      onUpdate,
      ArticleTable
  } = useArticleTable({
    openEditFormModal,
  })
</script>

<template>
  <page-layout>
    <template #header>
      <page-header>
        <a-button type="primary" @click="openFormModal">添加文章</a-button>
      </page-header>
    </template>
    <page-content>
      <ArticleTable />
      <ArticleFormModal @create-submit="handleCreate" @edit-submit="handleUpdate" />
    </page-content>
  </page-layout>
</template>

示例完整实现代码

What!!!我们的删除功能业务方法、编辑功能业务方法呢,哪里去了??我们维护的visible值、data值呢???我们可以不在import任何组件,emit也只是在底层的组件里声明了一次,不用反复声明emit来和父组件进行通信。

通过上面的示例,我们就算通过hook来编写我们的组件,也不会让我们的组件和业务逻辑进行隔离,这恰好很符合composition的思想,组合式的API会让你的逻辑更加清晰。

思考

  • 这种编写方式适合那种场景?
  • 我们的style怎么编写?
  • 其它缺点呢?

项目源码

如果喜欢文本,大家可以到此gitee(或者git)给个star。

一个后台管理系统的解决方案,简洁的代码、易扩展的组件,较全的配置环境。

总结

本文主要是介绍业务组件业务逻辑整合到HOOk中,这种vue3特有的编写方式也许会很大提升你团队的效率。

欢迎大家收藏.