@allo_shuang/modal
v1.0.9
Published
统一弹窗组件。用于规范项目弹窗管理的架构及使用。减少手动操作及心智成本。UI组件库使用Element-UI。
Downloads
40
Maintainers
Readme
弹窗组件
一. 背景及用途
当前问题:
工程组织问题:
- 项目中弹窗文件分布于各个业务代码中,很难实现复用,引用路径复杂。
- 各弹窗的显隐控制逻辑混杂在业务代码中,各种业务props传递复杂
- 父组件被隐藏时,弹窗也会被隐藏
前端效率问题:
针对弹窗这种可能一辈子也不会展示的组件:
- ElementUI默认底层使用v-show实现,依然会绘制到dom树中
- 理论上应该使用动态加载,不使用,不加载。减小最后打包的大小
心智成本:
- 个人风格及经验千差万别
- 弹窗公共逻辑重复编写,且容易出错。
- 新人接手旧项目,难以理解起外部逻辑及传入参数。
本组件主要解决的问题:
- 工程组织问题:
- 统一项目中所有弹窗文件位置至单一文件夹管理。
- 弹窗由统一单独的dom元素负责展示,全自动。
- 前端效率问题:
- 使用webpack中require.context省去了手动引入大量弹窗文件的工作,同时默认支持动态加载。用不到的弹窗,永不加载。
- 考虑到扩展性,提供四种优先级的公共参数配置
- 提供公共样式,loading状态,确定及取消逻辑,确定/取消按钮的显隐控制等
- 心智成本:
- 无需手动引用及注册组件,减少代码量。
- 所有业务相关参数在一个对象中传入,不用定义大量的props。
- 显隐逻辑自治,无需手动管理相关逻辑。
- 单一弹窗,单一逻辑。类似于纯函数,所有逻辑由传入的参数控制,便于不同的业务逻辑复用。
- 默认提供loading状态,处理异步逻辑时,只用关心业务接口。
- 工程组织问题:
二. 安装
1.安装库
npm i @allo_shuang/modal@latest -S
2.在src目录下新建文件夹/modal
3.在main.js文件中引用
import Modal from '@allo_shuang/modal'
Vue.use(Modal,{参数放这里...});
实际项目中,在/modal文件夹中创建index.js文件并在文件中引入了组件库,及自定义的样式文件
/modal
------index.js(在这里引入组件库及Vue.use传递统一配置参数,引入样式文件)
------style.scss
这样在main.js文件中只需引入@/modal即可。将配置及样式统一在modal文件夹中管理
###三. 使用
以新建弹窗ExampleModal为例
在src/modal文件夹中新建文件夹src/modal/ExampleModal并在其中创建index.vue文件。这个组件为弹窗中间部分显示的内容
index.vue内部定义props:
props: {params: Object},//params对应3中传入的参数
在任意想要使用弹窗的地方,调用:
this.$modal.show('ExampleModal', {组件内部需要的参数放这里,对应2的params})
四. 自定义选项
按照优先级,本组件有四个地方可以配置如下参数:
| 参数名 | 默认值 | 说明 | | ----------------- | ------ | ---------------------------------------- | | title | null | 弹窗标题 | | width | 640px | 宽度 | | cancelText | 取消 | 取消按钮文字,设为空字符串''时,按钮隐藏 | | okText | 确定 | 确定按钮文字,设为空字符串''时,按钮隐藏 | | showFooter | true | 确认/取消按钮所在的footer是否显示 | | closeOnClickModal | false | 同elementUI-Dialog的close-on-click-modal | | showClose | false | 同elementUI-Dialog的show-close | | customClass | ‘’ | 自定义的类名,用于个性化弹窗样式 |
参数优先级:
手动传入 > 组件定义 > 全局定义 > 默认值
唤起弹窗时传入的参数
this.$modal.show('ExampleModal', {放这里的参数优先级最高})
子组件内部定义的参数,与data,methods,生命周期等同级定义的参数
export default { title: '我是标题',<-------这里的优先级第二高 okText:'提交',<-------这里的优先级第二高 ... ... data() {return {}}, methods:{} }
全局定义的参数。Vue.use传入的参数中:
Vue.use(Modals, { title: '我是标题',<-------这里的优先级第三高 okText:'提交',<-------这里的优先级第三高 showFooter:false<-------这里的优先级第三高 ... })
默认值。如表中所列。代码在UniModal.vue中
const defaultParams = { title: null, width: '640px', cancelText: '取消', okText: '确定', showFooter: true, closeOnClickModal: false, showClose: true, customClass: '' }
五. 内置方法
组件methods中
- 如果定义了onCancel()方法,则会忽略默认的取消关闭操作
- onOK()为点击确定按钮时触发的方法,需要自己实现
关闭当前弹窗
this.$modal.close()
loading状态:loading状态下显示loading动画,屏蔽弹窗上所有交互操作,确认/取消按钮均不可点击。
- 常用于点击确定按钮后,发出网络请求。
this.$emit('updateLoading', true/false)
内置的确认弹窗:
this.$modal.text(params,callback):纯文本内容,无图标
this.$modal.success(params,callback):绿色✓图标+文本
this.$modal.warning(params,callback):黄色!图标+文本
this.$modal.error(params,callback):红色x图标+文本
this.$modal.info(params,callback):蓝色i图标+文本
上述五个方法的参数:
params:除自定义参数与上述内置参数外,额外增加三个参数:
- content:文本内容
- elementIcon:elementUI中Icon的名字,例如:'el-icon-warning',
- iconColor:图标的颜色
callback:点击确认的回调。形如(params, closeModal, setLoading) => {}
params:与第一个参数params相同,用于回传自定义参数
closeModal:关闭弹窗的方法。closeModal()
setLoading:控制弹窗的loading状态
//例如,依据companyId删除信息
this.$modal.warning(
{title:'警告',content:'确认删除公司信息吗?',width:'200px',companyId:'com-oiie-9839'},
(params, closeModal, setLoading) => {
setLoading(true)
api.deleteCompanyInfo(params.companyId).then(()=>{
setLoading(false)
closeModal()
})
})
)
六. 样式
除了手动传入的customClass类名外,默认提供类:modal-inner-class
可以在.modal-inner-class类下定义公共样式,在自定义类名下定义弹窗特异样式。
七. Q & A
文件夹是否只能叫/modal?
不是的,如果使用的是vue-cli创建的项目,可以在.env文件中自定义常量:
VUE_APP_MODAL_PATH = '自定义路径'
同时在引入组件库时,使用
import Modals from '@allo_shuang/modal/cli'
原理:require.context编译时,第一个参数为字符串,不接受变量及运算...
是否只支持Element-UI?
- 算是吧。因为默认使用的是el-dialog。理论上使用任何UI组件库都是可以的。这个组件最开始是React+AntD版的,现在使用VUE技术栈,顺便迁移过来的。
为什么看网络请求,发现弹窗对应的js文件请求了两次?
- 这是因为webpack对动态加载的文件做了prefetch以提高性能。
- 第一个请求可以在header中看到Purpose: prefetch
- 第二个请求可以看到size一栏目显示:(prefetch cache)
- 可以在webpack或者vue.config.js中禁用prefetch。这是另外的话题。
既然使用了Element-UI,为什么不直接使用this.$confirm方法而要提供默认的success等方法?
- 首先是为了将默认的这些方法统一归到默认dom下进行管理
- 使其支持普通弹窗接受的任何参数,减少心智负担
- 提供了loading等高级功能
- 默认的confirm不太支持高级定制
- 默认方法也是动态加载的,不使用不加载
为什么生成key要使用random方法?
- 即使是同名的弹窗,也要使用不同的key来保证单个弹窗的独立性
- 不排除将来同时打开两个同名的弹窗
代码中的overlay参数是干什么的?
- 你居然发现了隐藏参数!!!
- 默认同一时间只有一个弹窗存在。如果打开弹窗时,传入overlay:true,则弹窗会同时最在。所以弹窗在容器中使用栈来保存的。同时closeAll方法也是因为同样的原因而存在。
- 规划中多弹窗的情况有很多功能及场景,预计在后续版本中放出,所以文档中暂时隐藏该参数。
为什么要自己写一个遮罩?
- 多个弹窗切换时,如果使用element-ui自带的遮罩,会出现闪屏的现象。所以自己实现一个遮罩在最下方,同时默认遮罩全部隐藏。
八. 相关技术点
如何自定义组件,Vue.use()的使用
require.context的使用及与动态加载的结合
Object.create(null)
实例化单文件组件及单独挂载:new + Vue.extend + .$mount() + document.body.appendChild
Vue挂载全局方法,Vue.prototype.xxx=
使用key属性来强制刷新/更新组件
获取动态加载组件内定义的值与方法:
component().then(module => { if (module && module.default) { module.default.xxxxxx }})
动态组件的使用
Vue-cli项目环境变量的规则:在.env文件中以VUE_APP_开头
九. TODO:
- [ ] 增加element-loading-text,element-loading-spinner,element-loading-background的支持
- [ ] 多弹窗展示时的遮盖,显隐,参数传递等
- [ ] 增加默认的样式代码以适配常见场景,例如弹窗中,form的input长度改为100%等