乐趣区

关于前端:手把手教你以最简单的方式封装-Vue3-模态Modal-类组件

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

在 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 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 的组件转换为具备 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
退出移动版