最佳实践:vue弹窗及滑块响应式

2021-08-30 10:14:49 浏览数 (1)

下述为项目中弹窗/滑块统一处理方式汇总(下述已 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 壳子在外,内容单独组件,后续组件可以替换为其他壳子

问题:

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

方案

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

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

代码语言:javascript复制
<my-component :visible.sync="centerDialogVisible" :id="editId" :key="editId">my-component>

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

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

代码语言:javascript复制
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 请求数据

代码语言:javascript复制
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 形式

代码语言:javascript复制
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() 还原

代码语言:javascript复制
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

0 人点赞