反馈型组件,是前端常常波及的组件,它们容许向用户提供揭示、提供更多选项或增加额定信息,而不会弄乱次要内容。

在 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)无非就是利用了 vuerender 能力,将一个组件间接渲染至 html 当中,但理论做起来还是相当麻烦,须要解决 render销毁的机会,还须要想方法将处理函数传递给组件本身,且无奈和组件申明模式相结合应用。

并且思考在理论我的项目开发当中,咱们可能并不会思考重头实现一个 dialog 组件,而是应用第三方组件库自带的去魔改,实际上就会遇到各种各样的问题。

综上思考老本还是过高,但还是有可解决的思路,所以咱们明确以下几点要求:

  • 组件可反对组件申明、函数调用两种模式(declarative and imperative
  • 定制化能力强、开发成本低,以最简略的形式制作模态类组件
  • 应用现有组件库(如 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 的组件转换为具备 Providerrender 函数

useOverlayCalltransformOverlay 的变体写法,间接作用就是间接调取命令回调。

基于第三方组件库定制化

以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