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
}
}
如果有更好的意见建议请一起讨论交流!!!