验证vue2组件拆分对性能的影响与重现代码分享

842 阅读1分钟

一直都知道单个vue组件中html元素过多,当发生数据更新时,会导致界面卡顿。网上的文章中有很多这类案例的文章,实际工作中我也遇到过,但自己从来没有在闲暇时(或者说自己的示例项目中),重现过,今天就要写个文章来重现这个场景。

示例

渲染200个表单项的界面

方式一:一次性渲染200个表单项

结论: 在输入框中输入文字时,能够明显的感觉到卡顿

<template>
  <div>
    <FormTemplate :count="200"></FormTemplate>
  </div>
</template>

<script>
import FormTemplate from "./FormTemplate";
export default {
  name: "StyleTextPage",
  components: {
    FormTemplate,
  },
  data() {
    return {};
  },
  created() {},
  methods: {},
};
</script>

<style scoped lang="scss"></style>

方式二:200表单项目,分成4个组件实现,每个50个表单项

结论: 在输入框中输入文字时,丝般顺滑

<template>
  <div>
    <!--    <FormTemplate :count="200"></FormTemplate>-->
    <FormTemplate></FormTemplate>
    <FormTemplate></FormTemplate>
    <FormTemplate></FormTemplate>
    <FormTemplate></FormTemplate>
  </div>
</template>

<script>
import FormTemplate from "./FormTemplate";
export default {
  name: "StyleTextPage",
  components: {
    FormTemplate,
  },
  data() {
    return {};
  },
  created() {},
  methods: {},
};
</script>

<style scoped lang="scss"></style>

基础代码

DynForm.vue: 一个简单的动态表单组件(基于el-form)

<template>
  <el-form :model="formModel" v-bind="elFormAttrs">
    <el-form-item
      v-for="(formItemConfig, index) in formItemConfigArr"
      :key="`${formItemConfig.prop}-${index}`"
      :prop="formItemConfig.prop"
      :rules="formItemConfig.rules"
      :label="formItemConfig.label"
    >
      <el-input
        v-if="formItemConfig.itemType === 'input'"
        v-model="formModel[formItemConfig.prop]"
      ></el-input>
      <el-select
        v-else-if="formItemConfig.itemType === 'select'"
        v-model="formModel[formItemConfig.prop]"
      >
        <template v-if="formItemConfig.optionArr">
          <el-option
            v-for="(option, pos) in formItemConfig.optionArr"
            :key="`${option.value}-${pos}`"
            :value="option.value"
            :label="option.label"
          ></el-option>
        </template>
      </el-select>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  name: "DynForm",
  model: {
    event: "change",
    prop: "formData",
  },
  props: {
    formData: {
      type: Object,
    },
    /**
     * prop: 唯一标识
     * itemType: 表单项类型
     * rules: 表单验证规则
     * label: 显示标签
     * optionArr: 下拉值
     */
    formItemConfigArr: {
      type: Array,
      default: () => [],
    },
    // el-form支持的所有属性
    elFormAttrs: {
      type: Object,
    },
  },
  data() {
    return {
      formModel: this.formData ? this.formData : {},
    };
  },
  watch: {
    formData: {
      handler(newVal) {
        this.formModel = newVal;
      },
    },
  },
  methods: {},
};
</script>

<style scoped></style>

FormTemplate.vue: 根据count数量生成一个大表单

<template>
  <div>
    <DynForm
      v-model="formData"
      :form-item-config-arr="formItemConfigArr"
      :el-form-attrs="elFormAttrs"
    ></DynForm>
  </div>
</template>

<script>
import DynForm from "@/components/form/dyn/DynForm";
export default {
  name: "FormTemplate",
  components: {
    DynForm,
  },
  props: {
    // input文本框数量
    count: {
      type: Number,
      default: 50,
    },
  },
  data() {
    // 表单项配置
    const formItemConfigArr = [
      {
        prop: "name",
        label: "姓名",
        itemType: "input",
      },
      {
        prop: "sex",
        label: "性别",
        itemType: "select",
        optionArr: [],
      },
    ];
    for (let i = 0; i < this.count; i++) {
      formItemConfigArr.push({
        prop: "name" + i,
        label: "姓名",
        itemType: "input",
      });
    }
    return {
      elFormAttrs: {
        inline: true,
      },
      formData: null,
      formItemConfigArr,
    };
  },
  created() {
    this.loadFormData();
    this.loadSexOptions();
  },
  methods: {
    loadFormData() {
      setTimeout(() => {
        this.formData = {
          name: "张三",
          sex: 1,
        };
      }, 1000);
    },
    loadSexOptions() {
      setTimeout(() => {
        const item = this.formItemConfigArr.find((item) => item.prop === "sex");
        if (item) {
          const sexOptionArr = [
            { value: 1, label: "男" },
            { value: 2, label: "女" },
            { value: 3, label: "未知" },
          ];
          item.optionArr = sexOptionArr;
        }
      }, 1500);
    },
  },
};
</script>

<style scoped lang="scss">
.js-validate-form ::v-deep(.is-error .o-show-data) {
  color: red;
}
</style>