反馈型组件,是前端常常波及的组件,它们容许向用户提供揭示、提供更多选项或增加额定信息,而不会弄乱次要内容。
在 Vue 中 Modal 类组件应用通常分为两种形式
- 组件申明模式(
declarative
)
<template>
<dialog v-model="show" @ok="onOk" @close="onClose">
<div class="model__title">title</div>
<div class="model__conent">content</div>
</dialog>
</template>
<script>
import {defineComponent} from 'vue'
export default defineComponent({setup() {const show = ref(false)
const onOk = () => {// ...}
const onClose = () => {// ...}
return {show, onOk, onClose}
}
})
</script>
长处:定制化水平高、限度低,能够最大水平的施展主管设计想法
毛病:复用性差、应用频繁时须要定义多份 {show, onOk, onClose}
代码,代码冗余。
- 函数调用模式(
imperative
)
Modal.confirm({
title: '...',
contnet: '...'
})
.then(() => {/* ok */})
.catch(() => { /* close */})
长处:数据独立与页面独立、复用性、通用性强、业务代码流程易懂、无奈继承上下文
毛病:波及到定制化将很难解决,往往须要编写 template
字符串 / jsx
代码
弹出层组件波及思路
说了那么多,也该进入主题了。在前端倒退至今为止,这类组件的封装曾经有了宽泛的累积,
其中函数调用(imperative
)无非就是利用了 vue
的 render
能力,将一个组件间接渲染至 html 当中,但理论做起来还是相当麻烦,须要解决 render
和 销毁
的机会,还须要想方法将处理函数传递给组件本身,且无奈和组件申明模式相结合应用。
并且思考在理论我的项目开发当中,咱们可能并不会思考重头实现一个 dialog
组件,而是应用第三方组件库自带的去魔改,实际上就会遇到各种各样的问题。
综上思考老本还是过高,但还是有可解决的思路,所以咱们明确以下几点要求:
- 组件可反对组件申明、函数调用两种模式(
declarative
andimperative
) - 定制化能力强、开发成本低,以最简略的形式制作模态类组件
- 应用现有组件库(如
element-plus
)集成和定制化性能 - 反对组件继承全局利用上下文
那么基于下面几点,一般原始(vue render
/ tempalte
)的形式封装就不能满足咱们的需要,咱们须要一个趁手的工具帮咱们做这些事件。
笔者将这套逻辑抽离了进去,造成了一套残缺的 Vue3 弹出层解决方案 unoverlay-vue,它能够满足咱们上述的所有要求。
对于实现基层原理方面,本文不作过多展现,能够查看笔者以往文章 # Vue3 模态框封装计划
⚙️ Install
pnpm add unoverlay-vue
# Or Yarn
yarn add unoverlay-vue
在 main.js 中全局装置能够使所有弹出层继承上下文
// main.js
import {createApp} from 'vue'
import unoverlay from 'unoverlay-vue'
import App from './App.vue'
const app = createApp(App)
app.use(unoverlay)
app.mount('#app')
实现根底的 Modal 性能
定义 Model 组件,这里以最简案例实现,不蕴含动画逻辑(能够应用 <Transition>
实现)
你能够大胆地施展你的想象力
<!-- Model.vue -->
<template>
<!-- 动画能够应用 -->
<div v-show="visible">
<div>{{title}}</div>
<button @click="confirm(`${title}:confirmed`)"> confirm </button>
<button @click="cancel()"> cancel </button>
</div>
</template>
<script setup>
import {defineEmits, defineProps} from 'vue'
import {useOverlayMeta} from 'unoverlay-vue'
const props = defineProps({
title: String,
// 如果您想将其用作 template 中的组件应用,
// 你须要在 props 中定义 visible 字段
visible: Boolean
})
// 定义组件中应用的事件(可选)// 在组件中应用会有事件提醒
defineEmits(['cancel', 'confirm'])
// 从 useOverlayMeta 获取 Overlay 信息
const {visible, confirm, cancel} = useOverlayMeta({
// 弹出层动画的持续时间, 能够防止组件过早被销毁
// 仅在 template 中应用则不须要定义
animation: 1000
})
</script>
创立函数调用回调(imperative
), 在 Javascript
/ Typescript
中调用
import {transformOverlay} from 'unoverlay-vue'
import Model from './Model.vue'
// 转换函数调用模式(imperative)const ModelCallbck = transformOverlay(Model)
// 调用并获取 confirm 回调的值
const value = await ModelCallbck({title: 'callbackOverlay'})
// value === "callbackOverlay:confirmed"
或者任意中央间接调起组件
import {useOverlayCall} from 'unoverlay-vue'
import Model from './Model.vue'
const value = await useOverlayCall(Model, {props: { title: 'useOverlay'}
})
// value === "useOverlay:confirmed"
或者在 template
中以组件申明模式(declarative
)应用
<template>
<Model
v-model:visible="visible"
title="useTemplate"
@confirm="confirm"
@cancel="cancel"
/>
<script setup>
import Model from './Model.vue'
const visible = ref(false)
const confirm = (value) => {/* value === 'useTemplate:confirmed' */}
const cancel = () => {/*...*/}
</script>
useOverlayMeta
是 unoverlay-vue 的外围函数,它基于 Vue 的依赖注入实现
- 在
transformOverlay
转换后,外部应用Provider
携带的具备销毁组件本身、调取Propmise
的性能 - 在
template
中应用,外部应用组件的model
,调用组件本身的emits
(confirm,cancel)
transformOverlay
能够将应用 useOverlayMeta
的组件转换为具备 Provider
的 render
函数
useOverlayCall
是 transformOverlay
的变体写法,间接作用就是间接调取命令回调。
基于第三方组件库定制化
以 element-plus@2.15.7(dialog)为例(你也能够应用其余组件库)
<!-- MyDialog.vue -->
<template>
<el-dialog v-model:visible="visible" :title="title" @close="cancel()">
<!-- 你的定制化内容 -->
<button @click="confirm(`${title}:confirmed`)" />
</el-dialog>
</template>
<script setup>
import {defineEmits, defineProps} from 'vue'
import {useOverlayMeta} from 'unoverlay-vue'
const props = defineProps({title: String,})
const {visible, confirm, cancel} = useOverlayMeta({animation: 2000})
</script>
创立函数调用回调(imperative
), 在 Javascript
/ Typescript
中调用
import {transformOverlay} from 'unoverlay-vue'
import MyDialog from './MyDialog.vue'
const MyDialogCallback = transformOverlay(MyDialog)
const value = await MyDialogCallback({title: 'myElDialog'})
// value === "myElDialog:confirmed"
怎么样,应用起来是不是超简略,还反对 Typescript
和自定义 appContext
,
更多用法能够参考 unoverlay-vue 中文文档
总结与思考
作为一名前端优良的前端工程师, 面对各种繁琐而反复的工作, 咱们不应该循序渐进的去 ” 辛勤劳动 ”, 而是要依据已有前端的开发教训, 总结出一套的高效开发的办法。
咱们总有更好的方法和思路实现,不是吗~
- github: TuiMao