hooks理解与实践----个人总结

254 阅读3分钟

1.什么是hooks

按照约定 以xxx开头(大部分场景是use,项目自行约定另算)的一个函数,让你可以不用class的写法来实现class类似的封装调用(里面可以包含逻辑,生命周期,回调,变量等等你能装的所有内容来帮助你完成功能的复用以及调用)同时他是纯函数式的

2.hooks应该怎么书写

举一个例子:我现在需要写一个画板,里面会有很多的方法,状态和逻辑,并且我有好几个地方都要使用类型的功能,就可以把他变成一个hooks

这是TS部分 这就是一个简单的hooks

    
export function useCanvas() {
    /* canvas绑定的ref dom等等 都可以*/
    const canvasRef =ref<HTMLCanvasElement>()
    const canvas2D = ref<CanvasRenderingContext2D | null | undefined>(null) 
    /* 初始化 */
    function init() {
        canvas2D.value = canvasRef.value?.getContext('2d')
    }
    /** 画点啥 */
    function drew() {
        // 画点啥
    }
    /** 擦除画板 */
    function clearArtboard() {
        
    }
    return {
        canvasRef,
        canvas2D,
        init,
        drew,
        clearArtboard

    }
}

这里就完成了页面和hooks的关联

<template>
  <div class="hooks">
    <canvas ref="canvasRef"></canvas>
  </div>
</template>
<script lang="ts" setup>
import { defineComponent } from "vue";
import { useCanvas } from "./useCanvas";
const {canvas2D,canvasRef,drew,clearArtboard} = useCanvas()
</script>
<style lang="scss" scoped>
.hooks {
  width: 100%;
  height: 100%;
}
</style>

这个是另外的调用hooks的地方

<template>
  <div class="hooks2">
       <div class="operation_bar">
          <button @click="drew">画一下</button>
          <button @click="clearArtboard">清空</button>
      </div>
      <div>
          <canvas ref="canvasRef"></canvas>
      </div>
  </div>
</template>
<script lang="ts" setup>
import { defineComponent } from "vue";
import { useCanvas } from "./useCanvas";
const {canvas2D,canvasRef,drew,clearArtboard} = useCanvas()
</script>
<style lang="scss" scoped>
.hooks {
  width: 100%;
  height: 100%;
}
</style>

通过这种方式 就实现了 hooks和dom的组合 同时 useCanvas里面只需要处理逻辑 提供方法 其他所有的内容都是页面去组合,类似于乐高的模式,你提供零件单元,最后拼成什么样取决于想象力,如果你的项目中有很多可以复用的内容,全部都可以使用hooks的方法来实现 能够极大的减少代码的重复编写

注:hooks只是一种写法和思路无论是什么框架,都可以使用这套方法和思路来进行逻辑的编写以及业务功能的抽离

3.一个符合常见业务场景的hooks(增删改查的业务)

这里完成了一些基本的逻辑封装 大概的模拟了一下真正的场景以及可能用到的内容 根据自己的实际去拓展维护 也可以封装多个hooks进行组合使用 hooks里面是可以使用hooks的

import {  ref} from "vue";
export type TypeEnum = "add"|'edit'|'copy'
export interface IColumnItem {
    key?:any
    dataKey:any
    title:any
    width?:any
}
export interface IDialogConfig {
    show:boolean,
    title:any
    type:TypeEnum
}
export interface IUseTableProps{
 api:any,
 column:IColumnItem[],
 dialogTitle:string
}
export function useTable<T=any,K=any>(Props:IUseTableProps) {
    /* 编辑场景的额外参数  我不想写类型了 用方法去做也可以  方式多种多样*/
    const editOtherParams = ref<any>({})
    /* 弹出内表单的ref 类型写你ui库的表单实例的类型 el就写el antd就写antd */
    const dialogFormRef = ref()

        /* 表格的搜索提交 */
        const tableApiParams = ref<T>()
        /* 表格的配置 */
        const column:IColumnItem[] = Props.column
        /* 新增编辑弹出的配置 */
        const dialogConfig = ref<IDialogConfig>({
            show:false,
            title:'',
            type:'add'
        })
        /* 新增编辑弹框的表单配置 */
        const dialogFormModel = ref<K>()


        /* 打开弹窗 */
        function handleOpenModel<TRow=any>(type:TypeEnum,row?:TRow) {
            const titleObj:Record<TypeEnum,string> = {
                add:`新增${Props.dialogTitle}`,
                edit:`编辑${Props.dialogTitle}`,
                copy:`复制${Props.dialogTitle}`,
            }
            if(type!=='add'){
                editOtherParams.value =row
            }
            dialogConfig.value.title = titleObj[type]
            dialogConfig.value.type = type
            dialogConfig.value.show = true
        }   
        function getTableData() {
            // 这里你应该还要验证是不是函数 是不是 Promise 等等 我偷懒了
        
            if(Props.api){
                Props.api(tableApiParams).then((res:{code:string,data:any})=>{
                    // 这里给表格的数据赋值
                })
            }
        }
        /* 关闭弹窗 */
        function closeDialogFormModel() {
            dialogConfig.value.title = ''
            dialogConfig.value.type = 'add'
            dialogConfig.value.show = false
            dialogFormModel.value ={}
        }
        /* 提交 */
       async function submit() {
          const status =   dialogFormRef.value?.validate()
          if(status){
            // 做提交的动作
          }
        }
        return{
            tableApiParams,
            column,
            handleOpenModel,
            getTableData,
            closeDialogFormModel,
            dialogConfig,
            dialogFormModel,
            dialogFormRef,
            submit
        }

}
<template>
  <div class="tableHooks">
    <!-- 业务自己的table  -->
    <div>
      <button @click="handleOpenModel('add')">新增</button>
      <button @click="handleOpenModel('edit', { id: 1 })">编辑</button>
      <button @click="handleOpenModel('copy', { id: 1 })">复制</button>
      <button @click="closeDialogFormModel()">关闭</button>
    </div>
    <!-- 模拟弹窗 -->
    <div v-model="dialogConfig.show" :title="dialogConfig.title">
      <!-- 模拟表格 -->
      <div>
        <!-- 绑定表格数据 -->
        <div :modelValue="dialogFormModel"></div>
        <!-- 提交按钮 -->
        <div>
          <button @click="submit()">提交</button>
        </div>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useTable } from "./useTable";
interface ITableParams {
  name: string;
  city: any;
}
/* 模拟接口请求 */
async function getTableData(params: ITableParams) {
  return {
    code: "200",
    data: [],
  };
}
const {
  handleOpenModel,
  dialogConfig,
  closeDialogFormModel,
  dialogFormModel,
  submit,
} = useTable<any, ITableParams>({
  api: getTableData,
  column: [{ dataKey: "name", title: "名字" }],
  dialogTitle: "学生数据",
});
</script>
<style lang="scss" scoped>
.tableHooks {
  width: 100%;
  height: 100%;
}
</style>

4.多个hooks组合式使用

这样也是完全ok的 因为你只是相当于在函数内部调用了另一个函数而已 理论上只要你想 可以有无数个hooks来进行组合

import { useCanvas } from "./useCanvas";
/* hooks进行组合 */
export function useXXX() {
    const {canvas2D,canvasRef,drew,clearArtboard} = useCanvas()
    /* 做点啥 */
    function doWhere() {
        drew()
    }
    return{
        canvas2D,canvasRef,doWhere
    }
}

如果有更好的意见建议请一起讨论交流!!!