下述为项目中弹窗/滑块统一处理方式汇总(下述已 el-dialog 为例)
Demo 地址:https://codesandbox.io/s/thirsty-sun-eugvd?file=/src/components/User.vue:2122-2497
DOM 结构
方式1. (推荐1:直接注入数据「详情」) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子
<template><el-dialogtitle="编辑":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 壳子在外,内容单独组件,后续组件可以替换为其他壳子
<template><el-dialogtitle="编辑":visible.sync="centerDialogVisible"width="30%"center><!-- 自定义组件(包含保存、取消按钮) --><my-component :visible.sync="centerDialogVisible" :id="editId"></my-component></el-dialog></template>
方式3. (不推荐) el-dialog 在组件内部
<!-- 自定义组件(el-dialog在组件内) --><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为undefinedif (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 序列号与反序列(深拷贝)
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(当编辑比新增属性多很多时,该方案会很冗余)
// 需要获取当前是「新增」还是「编辑」操作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
