(关注前端精,让你前端越来越精 ~ )
先讲段话
一个灵活的,足够抽象的组件可以使我们提高编码效率,规范代码,统一 UI 风格...,在 Vue3 中,我们以常见的 Modal 对话框组件为例,探讨几个思路点与实现。
思考丨7个思路
- ✅ 一个对话框需要的基本要素「标题,内容,确定/取消按钮」。内容需要灵活,所以可以是字符串,或一段 html 代码(也就是 slot )。
- ✅ 对话框需要“跳出”,避免来自父组件的“束缚”,用 Vue3
Teleport
内置组件包裹。 - ✅ 调用对话框需要在每个父组件都进行引入
import Modal from '@/Modal'
,比较繁琐。考虑还可以采用 API 的形式,如在 Vue2 中:this.$modal.show({ /* 选项 */ })
。 - ✅ API 的形式调用,内容可以是字符串,灵活的
h
函数,或jsx
语法进行渲染。 - ✅ 可全局配置对话框的样式,或者行为...,局部配置可进行覆盖。
- ✅ 国际化,可灵活与
vue-i18n
糅合,即:如果没有引入vue-i18n
默认显示中文版,反之,则会用vue-i18n
的t
方法来切换语言。 - ✅ 与 ts 结合,使基于 API 的形式调用更友好。
思路有了就让我们来做行动的巨人~
实践
Modal 组件相关的目录结构
├── plugins
│ └── modal
│ ├── Content.tsx // 维护 Modal 的内容,用于 h 函数和 jsx 语法
│ ├── Modal.vue // 基础组件
│ ├── config.ts // 全局默认配置
│ ├── index.ts // 入口
│ ├── locale // 国际化相关
│ │ ├── index.ts
│ │ └── lang
│ │ ├── en-US.ts
│ │ ├── zh-CN.ts
│ │ └── zh-TW.ts
│ └── modal.type.ts // ts类型声明相关
说明:因为 Modal 会被 app.use(Modal)
调用作为一个插件,所以我们把它放在 plugins 目录下。
Modal.vue 的基础封装(只展示template)
<template>
<Teleport to="body":disabled="!isTeleport">>
<div v-if="modelValue"class="modal">
<div class="mask":style="style"
@click="handleCancel">div>
<div class="modal__main">
<div class="modal__title">
<span>{
{title||'系统提示'}}span>
<span v-if="close"title="关闭"class="close"
@click="handleCancel">✕span>
div>
<div class="modal__content">
<Content v-if="typeof content==='function'":render="content" />
<slot v-else>
{
{content}}
slot>
div>
<div class="modal__btns">
<button :disabled="loading"
@click="handleConfirm">
<span class="loading"v-if="loading"> ❍ span>确定
button>
<button @click="handleCancel">取消button>
div>
div>
div>
Teleport>
template>
说明:从 template 我们可以看到,Modal 的 dom 结构,有遮罩层、标题、内容、和底部按钮几部分。这几块我们都可以定义并接收对应 prop 进行不同的样式或行为配置。
现在让我们关注于 content(内容)这块:
<div class="modal__content">
<Content v-if="typeof content==