下述为项目中弹窗/滑块统一处理方式汇总(下述已 el-dialog 为例

Demo 地址:https://codesandbox.io/s/thirsty-sun-eugvd?file=/src/components/User.vue:2122-2497

DOM 结构

方式1. (推荐1:直接注入数据「详情」) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子

  1. <template>
  2. <el-dialog
  3. title="编辑"
  4. :visible.sync="centerDialogVisible"
  5. width="30%"
  6. center>
  7. <!-- 自定义组件 -->
  8. <my-component :visible.sync="centerDialogVisible" :data="xxxx"></my-component>
  9. <!-- 弹窗按钮 -->
  10. <span slot="footer">
  11. <el-button @click="centerDialogVisible = false">取 消</el-button>
  12. <el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
  13. </span>
  14. </el-dialog>
  15. </template>

方式2. (推荐2) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子

  1. <template>
  2. <el-dialog
  3. title="编辑"
  4. :visible.sync="centerDialogVisible"
  5. width="30%"
  6. center>
  7. <!-- 自定义组件(包含保存、取消按钮) -->
  8. <my-component :visible.sync="centerDialogVisible" :id="editId"></my-component>
  9. </el-dialog>
  10. </template>

方式3. (不推荐) el-dialog 在组件内部

  1. <!-- 自定义组件(el-dialog在组件内) -->
  2. <my-component :visible.sync="centerDialogVisible" :id="editId"></my-component>

响应方式

前提:组件内不请求数据推荐1:直接注入数据「详情」

如果是将当前 row 或者 组件外获取数据,直接传递就好(详情通常是这种场景),不在本文讨论范围内。

举例:新增/编辑使用同一组件 — (推荐2) el-dialog 壳子在外,内容单独组件,后续组件可以替换为其他壳子

问题:

  1. 数据响应触发请求,特别注意同一ID以及区分新增(无需请求)/编辑情况
  2. 表单错误提示语清空,特别注意第一次调用时,$refs['xxx'] 可能还不存在

方案

方案1. (禁用) v-if + created/mounted 写法简单,但是性能极差,万不得已杜绝使用!!!

方案2. (不建议) :key 虽性能上会比 v-if 好很多,但是这里的场景,数据响应即可解决,和 DOM(虚拟DOM)没太大关系。

  1. <my-component :visible.sync="centerDialogVisible" :id="editId" :key="editId"></my-component>

方案3. (不建议) refs.xxx.init() 调用。两个问题:第一次调用时机,往往需要 await this.$nextTick();组件内部方法被外部元素调用,耦合度高,维护和升级成本无法控制。

方案4. 监听 id 变化,然后根据 id 请求数据
编辑保存成功,点击当前行,id 并未发生变化,导致数据为变更前的 ==> 保存成功后,id = undefined,取消或者关闭不做处理。

  1. props: ['id'],
  2. watch: {
  3. id: {
  4. handler (val, oldVal) {
  5. // 新增id为undefined
  6. if (val) {
  7. this.requestData(val) // 请求后台
  8. }
  9. },
  10. immediate: true
  11. },
  12. visible: {
  13. handler (val, oldVal) {
  14. !val && this.resetForm()
  15. },
  16. immediate: true
  17. }
  18. }
  • 优势:请求精准,不会存在额外请求情况
  • 劣势: 需要监听 id 和 watch,处理稍许复杂

方案5. 监听 visible 变化,然后根据 visible 请求数据

  1. props: ['id', 'visible'],
  2. watch: {
  3. visible: {
  4. handler(val, oldVal) {
  5. !val && this.resetForm()
  6. if (val && this.id) {
  7. this.getUserById()
  8. }
  9. },
  10. immediate: true
  11. }
  12. }
  • 优势: 错误提示清除,统一处理了,实现简单
  • 劣势: 同一ID编辑,第一次不保存(信息未发生变化),第二次打开仍会请求

补充:

针对上述「方案5」,切换 ID 弹窗显示状态不发生变化,需要同时监听 ID。

下述采用了 watch function 的形式;这里同时可以采用 computed + watch 形式

  1. created() {
  2. this.unwatch = this.$watch(
  3. () => {
  4. return [this.id, this.visible];
  5. },
  6. (val, oldVal) => {
  7. let [id, visible] = val
  8. !visible && this.resetForm();
  9. if (visible && id) {
  10. this.getUserById();
  11. }
  12. },
  13. {
  14. immediate: true
  15. }
  16. );
  17. },
  18. beforeDestroy () {
  19. this.unwatch()
  20. }

延展问题

编辑与新增公用同一弹窗,先编辑,后新增,由于当前弹窗是v-show形式导致保存时多余属性被提交了(如ID)。
特别注意:el-form resetFields() 并不是处理该场景,其只会重置 el-form-item prop属性值

方案1:(繁琐) 将 form 对象进行 JSON 序列号与反序列(深拷贝)

  1. const userForm = {
  2. name: "",
  3. province: ""
  4. }
  5. export default {
  6. name: "User",
  7. props: ["visible", "id"],
  8. data() {
  9. return {
  10. userForm: JSON.parse(JSON.stringify(userForm)),
  11. rules: { ... }
  12. };
  13. },
  14. methods: {
  15. resetForm() {
  16. this.$refs.userForm.resetFields();
  17. this.userForm = JSON.parse(JSON.stringify(userForm));
  18. }
  19. }
  20. }

方案2:(局限) 提交时,删除 id(当编辑比新增属性多很多时,该方案会很冗余)

  1. // 需要获取当前是「新增」还是「编辑」操作
  2. delete this.userForm.id
  3. ...

方案3:(推荐) 通过 this.$options.data() 还原

  1. resetForm() {
  2. this.$refs.userForm.resetFields();
  3. this.userForm = Object.assign({}, this.$options.data().userForm);
  4. }
  5. // 或更霸道,还原所有 this 数据
  6. const data = this.$options.data()
  7. Object.keys(data).forEach(key => {
  8. this[key] = data[key]
  9. })

参考地址