下述为项目中弹窗/滑块统一处理方式汇总(下述已 el-dialog 为例)
演示环境:https://eugvd.csb.app/ Demo 地址:https://codesandbox.io/s/thirsty-sun-eugvd?file=/src/components/User.vue:2122-2497
DOM 结构
方式1. (推荐1:直接注入数据「详情」) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子
代码语言:javascript复制<template>
<el-dialog
title="编辑"
:visible.sync="centerDialogVisible"
width="30%"
center>
<my-component :visible.sync="centerDialogVisible" :data="xxxx">my-component>
<span slot="footer">
<el-button @click="centerDialogVisible = false">取 消el-button>
<el-button type="primary" @click="centerDialogVisible = false">确 定el-button>
span>
el-dialog>
template>
方式2. (推荐2) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子
代码语言:javascript复制<template>
<el-dialog
title="编辑"
:visible.sync="centerDialogVisible"
width="30%"
center>
<my-component :visible.sync="centerDialogVisible" :id="editId">my-component>
el-dialog>
template>
方式3. (不推荐) el-dialog 在组件内部
代码语言:javascript复制<my-component :visible.sync="centerDialogVisible" :id="editId">my-component>
响应方式
前提:组件内不请求数据 – 推荐1:直接注入数据「详情」
如果是将当前 row 或者 组件外获取数据,直接传递就好(详情通常是这种场景),不在本文讨论范围内。
举例:新增/编辑使用同一组件 – (推荐2) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子
问题:
- 数据响应触发请求,特别注意同一ID以及区分新增(无需请求)/编辑情况
- 表单错误提示语清空,特别注意第一次调用时,
$refs['xxx']
可能还不存在
方案
方案1. (禁用) v-if created/mounted
写法简单,但是性能极差,万不得已杜绝使用!!!
方案2. (不建议) :key
虽性能上会比 v-if
好很多,但是这里的场景,数据响应即可解决,和 DOM(虚拟DOM)没太大关系。
<my-component :visible.sync="centerDialogVisible" :id="editId" :key="editId">my-component>
方案3. (不建议) refs.xxx.init()
调用。两个问题:第一次调用时机,往往需要 await this.$nextTick()
;组件内部方法被外部元素调用,耦合度高,维护和升级成本无法控制。
方案4. 监听 id
变化,然后根据 id
请求数据
编辑保存成功,点击当前行,id 并未发生变化,导致数据为变更前的 ==> 保存成功后,id = undefined
,取消或者关闭不做处理。
props: ['id'],
watch: {
id: {
handler (val, oldVal) {
// 新增id为undefined
if (val) {
this.requestData(val) // 请求后台
}
},
immediate: true
},
visible: {
handler (val, oldVal) {
!val && this.resetForm()
},
immediate: true
}
}
- 优势: 请求精准,不会存在额外请求情况
- 劣势: 需要监听 id 和 watch,处理稍许复杂
方案5. 监听 visible
变化,然后根据 visible
请求数据
props: ['id', 'visible'],
watch: {
visible: {
handler(val, oldVal) {
!val && this.resetForm()
if (val && this.id) {
this.getUserById()
}
},
immediate: true
}
}
- 优势: 错误提示清除,统一处理了,实现简单
- 劣势: 同一ID编辑,第一次不保存(信息未发生变化),第二次打开仍会请求
补充: 针对上述「方案5」,切换 ID 弹窗显示状态不发生变化,需要同时监听 ID。
下述采用了 watch function
的形式;这里同时可以采用 computed watch
形式
created() {
this.unwatch = this.$watch(
() => {
return [this.id, this.visible];
},
(val, oldVal) => {
let [id, visible] = val
!visible && this.resetForm();
if (visible && id) {
this.getUserById();
}
},
{
immediate: true
}
);
},
beforeDestroy () {
this.unwatch()
}
延展问题
编辑与新增公用同一弹窗,先编辑,后新增,由于当前弹窗是v-show形式导致保存时多余属性被提交了(如ID)。
特别注意:el-form resetFields()
并不是处理该场景,其只会重置 el-form-item prop属性值
方案1:(繁琐) 将 form 对象进行 JSON 序列号与反序列(深拷贝)
代码语言:javascript复制const userForm = {
name: "",
province: ""
}
export default {
name: "User",
props: ["visible", "id"],
data() {
return {
userForm: JSON.parse(JSON.stringify(userForm)),
rules: { ... }
};
},
methods: {
resetForm() {
this.$refs.userForm.resetFields();
this.userForm = JSON.parse(JSON.stringify(userForm));
}
}
}
方案2:(局限) 提交时,删除 id(当编辑比新增属性多很多时,该方案会很冗余)
代码语言:javascript复制// 需要获取当前是「新增」还是「编辑」操作
delete this.userForm.id
...
方案3:(推荐) 通过 this.$options.data()
还原
resetForm() {
this.$refs.userForm.resetFields();
this.userForm = Object.assign({}, this.$options.data().userForm);
}
// 或更霸道,还原所有 this 数据
const data = this.$options.data()
Object.keys(data).forEach(key => {
this[key] = data[key]
})
参考地址
- Add a function to reset a component’s state – Vue官方Issues,其中有尤大的回复
- el-form-item和整个form清空值的问题 – ElementUI官方Issues