组件封装
任务描述
这个drawer做个二次封装。
要求:使用useDrawer打开Drawer,打开后使用useDrawerInner获取打开Drawer的参数。【最好组件参数也可以使用hook传进去,返回一个参数挂载到组件上】
任务分析
打开Drawer时使用useDrawer中提供的openDrawer方法,此方法可以传递初始化Drawer的配置项【属性】,同时提供getDrawerParams方法获取所传的所有参数。[这感觉有点怪怪的😂]
import { ref, reactive } from 'vue'; import { Drawer } from 'hui'; interface DrawerOptions { title?: string; visible?: boolean; } export function useDrawer():any { const drawerRef = ref<InstanceType<typeof Drawer>>() const drawerOptions = reactive<DrawerOptions>({ visible: false }) /** * 打开抽屉功能 * * 该函数用于配置并打开抽屉组件,允许通过传递部分抽屉选项来定制抽屉的行为和外观。 * 抽屉选项是一个对象,其中包含控制抽屉的各种属性,如是否可见、抽屉的位置等。 * 通过将传递的选项与当前抽屉选项合并,并将可见性设置为true,来实现打开抽屉的效果。 * * @param options 部分抽屉选项的对象。 * */ const openDrawer = (options: Partial<DrawerOptions>) => { Object.assign(drawerOptions, options, { visible: true }) }; const closeDrawer = () => { drawerOptions.visible = false }; /** * 获取抽屉配置参数。 * * 返回选项可能包括抽屉的大小、位置、可见性等。 * 通过调用此函数,可以获取到配置抽屉所需的所有参数,以便根据这些参数来动态展示或隐藏抽屉,或者调整抽屉的其他属性。 * */ const getDrawerParams = () => drawerOptions return { drawerRef, drawerOptions, openDrawer, closeDrawer, getDrawerParams, } }
组件内使用:
<template> <div> <HButton @click="openDrawer_">打开抽屉</HButton> <HDrawer v-model="drawerOptions.visible" :title="drawerOptions.title" :direction="drawerOptions.direction"> </HDrawer> </div> </template> <script setup lang="ts"> import { ref, reactive, toRefs, onMounted } from 'vue' import { useDrawer } from '../utils/useDrawer' const { drawerRef, drawerOptions, openDrawer, closeDrawer, getDrawerParams } = useDrawer() const openDrawer_ = () => { openDrawer({ title: '这是一个抽屉', direction: 'ltr' }) } </script> <style scoped lang="less"></style>
任务完善(7.9)
思路:
- 首先要把那个组件封装一下,用的时候直接用所封装的(比如叫XDrawer),不用原来的;
- 在封装的组件setup的时候抛出一个事件,事件参数就是一个函数(比如叫setParams),函数可以修改你所封装的组件里的参数,比如isVisible;
- 那么这个事件被谁接收呢,可以在useinnerDrawerInner里返回一个事件处理函数比如叫register,这样在useDrawerInner内部就拿到了setParams,可以控制组件内部比如显示与隐藏,那么useDrawerInner里面又可以返回closeDrawer函数,可以给XDrawer内部的关闭按钮使用;
- 在上面的register事件函数执行时,再抛出一个register事件,参数也为刚刚那个修改XDrawer属性的函数,那么这个事件可以被外部使用(比如你写了一个业务组件叫UserDrawer,里面用到了XDrawer,UserDrawer在首页被使用,那么在首页使用useDrawer),useDrawer返回一个register事件处理函数,处理前面的事件。那个在外面也可以做同样的时间控制打开与否,传递参数;
- 在此基础上可以扩展,比如XDrawer里面的loading遮罩层。
代码
XDrawer.vue
<template>
<div>
<HDrawer
v-model="internalState.isVisible"
v-bind="internalState"
></HDrawer>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, toRefs, onMounted } from "vue";
import { useDrawerInner } from "../utils/useInnerDrawer";
import { isVisible } from "hui/lib/es/utils/index.js";
const emits = defineEmits(["setParams"]);
const { register } = useDrawerInner();
const internalState = ref({
isVisible: false,
title: "Default Title",
});
const setParams = (params: any) => {
Object.assign(internalState.value, params)
return internalState.value
}
emits("setParams", setParams);
</script>
<style scoped lang="less"></style>
UserDrawer.vue
<template>
<div>
<p>封装组件</p>
<XDrawer @setParams="setparams"></XDrawer>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, toRefs, onMounted } from 'vue'
import XDrawer from './XDrawer.vue';
import { useDrawerInner } from '../utils/useInnerDrawer';
const emits = defineEmits(['register'])
const { register } = useDrawerInner()
const setparams = (fn: any) => {
register(fn, { isVisible: true })
emits('register', fn)
}
</script>
<style scoped lang="less"></style>
useInnerDrawer钩子
import { ref } from "vue";
interface Params {
isVisible?: boolean,
title?: string,
}
export function useDrawerInner() {
const paramsInner = ref<Params>()
const register = (fn: any, params: any) => {
paramsInner.value = fn(params)
}
const closeDrawer = () => {
console.log("closeDrawer")
}
return {
register,
closeDrawer
};
}
优化地方
emit放到useDrawerInner里面,每个业务组件都要写这个,这是重复的。
hook内无法使用emit函数啊!【defineEmits不行】
解决:可以使用getCurrentInstance方法获取实例后在使用.emit方法进行抛出。
这个在里面处理一下,是不是可以直接做事件函数。
任务完善(7.10)
内容
完成组件封装任务
完成抽屉关闭和抽屉打开的事件,同时可以控制loading的显示与否。
代码
-
XDrawer.vue
<template> <div> <HDrawer v-model="internalState.isVisible" v-bind="internalState" @open="internalState.handleOpenFun" @close="internalState.handleCloseFun"> <template #default> <div v-loading="internalState.loadingShow" style="width: 100%;height:200px"> loading </div> </template> </HDrawer> </div> </template> <script setup lang="ts"> import { ref } from "vue"; const emits = defineEmits(["setParams"]); const internalState = ref({ isVisible: false, title: "Default Title", handleOpenFun: null, handleCloseFun: null, loadingShow: false }); // const loadingShow = ref(true) const setParams = (params: any) => { Object.assign(internalState.value, params) return internalState.value } emits("setParams", setParams); </script> <style scoped lang="less"></style>
-
UserDrawer.vue
<template>
<div>
<p>封装组件</p>
<XDrawer @setParams="handleParams"></XDrawer>
</div>
</template>
<script setup lang="ts">
import XDrawer from './XDrawer.vue';
import { useDrawerInner } from '../utils/useInnerDrawer';
const { showLoading } = useDrawerInner()
const handleParams = (fn: any) => {
showLoading(fn)
}
</script>
<style scoped lang="less"></style>
-
Home.vue
<template> <div> <!-- <HButton @click="clickOpen">点击打开</HButton> --> <UserDrawer @register="(fn: any) => openDrawer(fn, () => { console.log('open') })"></UserDrawer> </div> </template> <script setup lang="ts"> import UserDrawer from './UserDrawer.vue' import { useDrawer } from '../utils/useDrawer'; const { openDrawer } = useDrawer() </script> <style scoped lang="less"></style>
-
useDrawer.ts
import { ref, getCurrentInstance } from "vue";
interface Params {
isVisible?: boolean;
title?: string;
handleOpenFun?: Function;
}
export function useDrawer() {
const instance = getCurrentInstance();
const paramsInner = ref<Params>();
/**
* 注册函数。
* @param fn 内部组件所抛出的修改配置函数。
* @param params 函数调用的参数。这是可选的,允许在调用注册的函数时传递额外的数据。
*/
const register = (fn: any, params?: any) => {
// 应用传入的函数到参数上,并更新内部参数的值
paramsInner.value = fn(params);
// 如果实例存在,则触发 'register' 事件,并传递函数作为参数
instance?.emit("register", fn);
};
/**
* 该函数用于触发打开抽屉的操作,并注册一个回调函数以处理相关的打开事件。
*/
const openDrawer = (fn: any, handleFunction: Function) => {
paramsInner.value = {
isVisible: true,
handleOpenFun: handleFunction,
};
register(fn, paramsInner.value);
};
return {
register,
openDrawer,
};
}
-
useInnerDrawer.ts
import { ref, getCurrentInstance } from "vue"; export function useDrawerInner() { const instance = getCurrentInstance(); const paramsInner = ref(); /** * 注册函数。 */ const register = (fn: any, params?: any) => { paramsInner.value = fn(params); instance?.emit("register", fn); }; /** * 关闭抽屉的操作,同时并注册一个回调函数以处理相关的关闭事件。 */ const closeDrawer = (fn: any, handleClose: Function) => { register(fn, { isVisible: false, handleCloseFun: handleClose, }); }; /** * 控制loading的展示功能 */ const showLoading = (fn: any) => { register(fn, { loadingShow: true }); }; return { register, closeDrawer, showLoading, }; }