反馈型组件,是前端常常波及的组件,它们容许向用户提供揭示、提供更多选项或增加额定信息,而不会弄乱次要内容。
在 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 Yarnyarn add unoverlay-vue
在 main.js 中全局装置能够使所有弹出层继承上下文
// main.jsimport { 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