函数式组件劫持替代json封装element表格

目录
  • 背景
  • 思路
  • 想法与实践
    • el-table-column获取
    • 如何渲染表格
    • 操作表格
  • 组件代码
  • 表格函数式组件
  • 问题点与优化
    • hf-table.vue
    • table.js
  • 后话

背景

系统有个整改需求,要求系统内的所有表格支持本地动态列显隐,拖拽排序列位置,固定列功能,涉及的页面很多

上效果图:

思路

其实最开始想的肯定是json配置表单的形式,再由循环出来的列去控制对应的位置和属性 但是!很多页面啊!每个页面都要去转json配置意味着大量的工作量和极高的风险

能不能我就写个自己的组件来包一层,这样我就能实现最小改动的情况下只需要替换组件标签来实现这个功能

与实际的不同只是我将原来的el-table换成了hf-table,同时支持原本el-table的所有功能

想法与实践

el-table-column获取

我们不可能去自己实现一个el-table的组件,所以无非我们的组件就是在el-table的基础上套一层壳,给他加上一个设置按钮,同时设置的内容能够去影响整个表格的渲染。

那既然我们不自己实现el-table则意味着原先代码中的el-table-column我们要拿到,并且要传给el-table,这样我们才能去渲染出来原先的那个表格

在一个组件的实例中,我们能够通过vnode去获取到当前的一个虚拟dom,vnode去获取到当前的一个虚拟dom,vnode去获取到当前的一个虚拟dom,vnode有一个componentOptions组件配置项的属性,通过他的children就能获取到所有的el-table-column 虚拟dom数组

如何渲染表格

上一步我们已经拿到了所有的el-table-column虚拟dom,那怎么将虚拟dom去渲染成对应的表格组件呢?

这不render就该登场了吗!!

这个children就是我们拿到的el-table-column的数组,我们只需要将该虚拟dom的数组以组件属性的形式传传进来了,再创建一个el-table,将对应的children传给他!卧槽,这不就又和原本<el-table>xxx</el-table>的效果一毛一样吗,是的 ,我做的就是挂羊头卖狗肉。

也就是说,实际上我的hf-table只是劫持了el-table,他的作用只是拿到原本写的el-table-colunm的虚拟dom,去渲染成一个表格

操作表格

此时我们的任务已经完成大半了,就是我原本el-table的标签已经可以被替换了,那我们要做的就只剩下操作表格了。 实际我做的很简单,既然我已经拿到了所有的子节点,那我就在hf-table组件中去操作成我想要的数组,再丢给render函数去渲染就好了

组件代码

整个组件的代码,代码量除掉样式也就不到100行

<template>
  <div class="hf-table">
    <el-popover
      placement="bottom-end"
      width="400"
      popper-class="table-cloumn-setting-popper"
      trigger="click"
    >
      <div class="setting-row-content">
        <draggable v-model="storageList" handle=".el-icon-s-operation" @end="updateTable">
          <div v-for="clo in storageList" :key="clo.label" class="setting-row">
            <i class="el-icon-s-operation" />
            <el-checkbox v-model="clo.show" class="label" @change="showOrHidden($event,clo)">{{ clo.label }}</el-checkbox>
            <el-button
              class="btn"
              size="mini"
              :type="clo.fixed === 'left' ? 'primary' : 'default'"
              @click="setFixed('left',clo)"
            >固定在左侧</el-button>
            <el-button
              class="btn"
              size="mini"
              :type="clo.fixed === 'right' ? 'primary' : 'default'"
              @click="setFixed('right',clo)"
            >固定在右侧</el-button>
          </div>
        </draggable>
      </div>
      <i slot="reference" class="el-icon-setting" />
    </el-popover>
    <new-table v-if="showTable" :config="config" />
  </div>
</template>
<script>
import draggable from 'vuedraggable'
import newTable from './table.js'
const components = { newTable, draggable }
export default {
  components,
  props: {
    storageName: {
      type: String,
      default: 'hfTable'
    }
  },
  data() {
    return {
      showTable: false,
      storageList: [],
      name: '',
      config: {
        children: [],
        attrs: {},
        listeners: {}
      }
    }
  },
  watch: {
    '$attrs': {
      handler(newV) {
        this.$set(this.config, 'attrs', newV)
      },
      deep: true,
      immediate: true
    }
  },
  mounted() {
    this.initStorage()
    this.updateTable()
  },
  methods: {
    showOrHidden(val, clo) {
      if (!val && this.storageList.filter(i => i.show).length === 0) {
        this.$message.warning('列表最少显示一列')
        this.$nextTick(() => {
          clo.show = true
        })
        return
      }
      this.updateTable()
    },
    setFixed(value, clo) {
      if (clo.fixed === value) {
        clo.fixed = false
      } else {
        clo.fixed = value
      }
      this.updateTable()
    },
    // 初始化缓存配置
    initStorage() {
      this.storageList = []
      const storage = window.localStorage.getItem(this.storageName)
      // 不管是否初次还是要做一下处理,万一页面有修改,做一下更新,以最新的node节点数组为准
      let list = storage ? JSON.parse(storage) : []
      this.$vnode.componentOptions.children.forEach(node => {
        // 以label为准,因为可能会改文本
        if (!node.componentOptions.propsData.type && list.findIndex(i => i.label === node.componentOptions.propsData.label) < 0) {
          // 不是特殊类型的 找不到就加上
          const propsData = JSON.parse(JSON.stringify(node.componentOptions.propsData))
          propsData.fixed = propsData.fixed !== undefined ? 'left' : false
          list.push({
            fixed: false, // 默认新增的都是不固定
            show: true, // 默认新增的都是显示的
            ...propsData
          })
        }
      })
      // 必须在节点数组存在的才有意义
      list = list.filter(item => this.$vnode.componentOptions.children.find(n => {
        return item.label === n.componentOptions.propsData.label
      }))
      this.storageList = list
    },
    // 根据缓存的数组进行渲染表格
    updateTable() {
      const childrenNodes = this.$vnode.componentOptions.children.filter(node => node.componentOptions.propsData.type)
      this.storageList.forEach(item => {
        if (item.show) {
          const node = this.$vnode.componentOptions.children.find(n => n.componentOptions.propsData.label === item.label)
          if (node) {
            node.componentOptions.propsData.fixed = item.fixed
            childrenNodes.push(node)
          }
        }
      })
      this.config.children = childrenNodes
      this.config.attrs = this.$attrs
      this.config.listeners = this.$listeners
      this.showTable = false
      this.$nextTick(() => {
        this.showTable = true
      })
      window.localStorage.setItem(this.storageName, JSON.stringify(this.storageList))
    }
  }
}
</script>
<style lang="scss" scoped>
  .table-cloumn-setting-popper{
    .setting-row-content{
      max-height: 600px;
      overflow-y: auto;
      .setting-row{
        height: 40px;
        line-height: 40px;
        .el-icon-s-operation{
          cursor: move;
          font-size: 16px;
          margin-right: 8px;
        }
        .label{
          margin-right: 8px;
        }
        .btn{
          padding: 4px!important;
        }
      }
    }
  }
  .hf-table{
    width:100%;
    height:100%;
    position: relative;
    .el-icon-setting{
      position: absolute;
      right: 20px;
      top:-20px;
      cursor: pointer;
    }
  }
</style>

表格函数式组件

import Vue from 'vue'
export default Vue.component('newtable', {
  functional: true,
  props: {},
  listeners: {},
  render: function(h, context) {
    return h(
      'el-table',
      {
        props: context.data.attrs.config.attrs,
        on: context.data.attrs.config.listeners
      },
      context.data.attrs.config.children
    )
  }
})

问题点与优化

当真的推行到项目中时,发现了以上代码存在了几个问题:

1.函数式组件没有生命周期和实例,也就是table.js帮我们渲染了el-table,我们却没办法拿到el-table 的实例,也就没办法去调用table原生的方法,例如clearSelection等

2.忘了做插槽传递。例如空数据自定义插槽等

hf-table.vue

<template>
  <div class="hf-table">
    <el-popover
      placement="bottom-end"
      popper-class="table-cloumn-setting-popper"
      trigger="click"
    >
      <div class="setting-row-content">
        <div style="text-align:right">
          <el-button @click="delAllStorage">恢复系统表格设置</el-button>
          <el-button @click="delStorage">恢复当前表格设置</el-button>
        </div>
        <draggable v-model="storageList" handle=".el-icon-s-operation" @end="updateTable">
          <div v-for="clo in storageList" :key="clo.label" class="setting-row">
            <i class="el-icon-s-operation" />
            <el-checkbox v-model="clo.show" class="label" @change="showOrHidden($event,clo)">{{ clo.label }}</el-checkbox>
            <el-button
              class="btn"
              size="mini"
              :type="clo.fixed === 'left' ? 'primary' : 'default'"
              @click="setFixed('left',clo)"
            >固定在左侧</el-button>
            <el-button
              class="btn"
              size="mini"
              :type="clo.fixed === 'right' ? 'primary' : 'default'"
              @click="setFixed('right',clo)"
            >固定在右侧</el-button>
          </div>
        </draggable>
      </div>
      <i slot="reference" class="el-icon-setting" />
    </el-popover>
    <!-- 按钮容器 -->
    <div
      class="table-operate-btn-content"
    >
      <!-- 插槽自定义表格上方操作栏 -->
      <slot name="operateBtnContent">
        <!-- 默认左右都有操作按钮,如果单纯想左或者想右,请在插入具名插槽 -->
        <div class="operate-btn-content">
          <!-- 流式左右布局 -->
          <slot name="btnContentLeft">
            <div />
          </slot>
          <slot name="btnContentRight">
            <div />
          </slot>
        </div>
      </slot>
    </div>
    <div :style="{height:`${tableHeight}px`}">
      <new-table v-if="showTable" :config="config" />
    </div>
  </div>
</template>
<script>
import draggable from 'vuedraggable'
import newTable from './table.js'
import setHeight from '@/mixins/setHeight'
const components = { newTable, draggable }
export default {
  name: 'HfTable',
  components,
  mixins: [setHeight],
  props: {
    storageName: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      showTable: false,
      storageList: [],
      name: '',
      config: {
        children: [],
        attrs: {},
        listeners: {}
      }
    }
  },
  watch: {
    '$attrs': {
      handler(newV) {
        this.$set(this.config, 'attrs', newV)
      },
      deep: true,
      immediate: true
    }
  },
  mounted() {
    this.initStorage()
    this.updateTable()
  },
  methods: {
    getInstance() {
      const ref = this.$children.find(i => i.$options._componentTag === 'el-table')
      return ref
    },
    delStorage() {
      this.$confirm('恢复当前表格设置将清除当前表格设置并刷新页面是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const storage = window.localStorage.getItem('tableStorage') ? JSON.parse(window.localStorage.getItem('tableStorage')) : {}
        storage[this.storageName] = []
        window.localStorage.setItem('tableStorage', JSON.stringify(storage))
        location.reload()
      })
    },
    delAllStorage() {
      this.$confirm('恢复系统表格设置将清除当前表格设置并刷新页面是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        window.localStorage.removeItem('tableStorage')
        location.reload()
      })
    },
    showOrHidden(val, clo) {
      if (!val && this.storageList.filter(i => i.show).length === 0) {
        this.$message.warning('列表最少显示一列')
        this.$nextTick(() => {
          clo.show = true
        })
        return
      }
      this.updateTable()
    },
    setFixed(value, clo) {
      if (clo.fixed === value) {
        clo.fixed = false
      } else {
        clo.fixed = value
      }
      this.updateTable()
    },
    // 初始化缓存配置
    initStorage() {
      this.storageList = []
      const storage = window.localStorage.getItem('tableStorage') ? JSON.parse(window.localStorage.getItem('tableStorage')) : {}
      // 不管是否初次还是要做一下处理,万一页面有修改,做一下更新,以最新的node节点数组为准
      let list = storage[this.storageName] ? storage[this.storageName] : []
      this.$vnode.componentOptions.children.forEach(node => {
        // 以label为准,因为可能会改文本
        if (!(!node.componentOptions || node.componentOptions.propsData.type) && list.findIndex(i => i.label === node.componentOptions.propsData.label) < 0) {
          // 非插槽且 不是特殊类型的 找不到就加上
          const propsData = JSON.parse(JSON.stringify(node.componentOptions.propsData))
          if (propsData.fixed === undefined || propsData.fixed === false) {
            propsData.fixed = false
          } else {
            propsData.fixed = propsData.fixed ? propsData.fixed : 'left'
          }
          list.push({
            fixed: false, // 默认新增的都是不固定
            show: true, // 默认新增的都是显示的
            ...propsData
          })
        }
      })
      // 必须在节点数组存在的才有意义
      list = list.filter(item => this.$vnode.componentOptions.children.find(n => {
        return n.componentOptions && item.label === n.componentOptions.propsData.label
      }))
      this.storageList = list
    },
    // 根据缓存的数组进行渲染表格
    updateTable() {
      // 特殊类型
      const childrenNodes = this.$vnode.componentOptions.children.filter(node => node.componentOptions && node.componentOptions.propsData.type)
      this.storageList.forEach(item => {
        if (item.show) {
          const node = this.$vnode.componentOptions.children.find(n => n.componentOptions && n.componentOptions.propsData.label === item.label)
          if (node) {
            node.componentOptions.propsData.fixed = item.fixed
            childrenNodes.push(node)
          }
        }
      })
      this.config.children = childrenNodes
      this.config.attrs = this.$attrs
      this.config.listeners = this.$listeners
      this.showTable = false
      this.$nextTick(() => {
        this.showTable = true
      })
      const storage = window.localStorage.getItem('tableStorage') ? JSON.parse(window.localStorage.getItem('tableStorage')) : {}
      storage[this.storageName] = this.storageList
      window.localStorage.setItem('tableStorage', JSON.stringify(storage))
    }
  }
}
</script>
<style lang="scss" scoped>
  .table-cloumn-setting-popper{
    .setting-row-content{
      max-height: 600px;
      overflow-y: auto;
      .setting-row{
        height: 40px;
        line-height: 40px;
        .el-icon-s-operation{
          cursor: move;
          font-size: 16px;
          margin-right: 8px;
        }
        .label{
          margin-right: 8px;
        }
        .btn{
          padding: 4px!important;
        }
      }
    }
  }
  .hf-table{
    width:100%;
    height:100%;
    position: relative;
    .el-icon-setting{
      position: absolute;
      right: 10px;
      top:16px;
      cursor: pointer;
    }
    .table-operate-btn-content{
      width: calc(100% - 40px);
     .operate-btn-content {
        height: 40px;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
    }
  }
</style>

针对插槽的处理主要是根据插槽没有componentOption属性,然后把它和带有type的这类vnode直接丢给el-table,而其他的再去做显隐的处理。

table.js

import Vue from 'vue'
export default Vue.component('newtable', {
  functional: true,
  props: {},
  listeners: {},
  render: function(h, context) {
    const scopedSlots = {}
    Object.keys(context.parent.$scopedSlots).forEach(key => {
      if (key !== 'default') {
        scopedSlots[key] = context.parent.$scopedSlots[key]
      }
    })
    return context.parent.$createElement(
      'el-table',
      {
        props: { ...context.data.attrs.config.attrs, ref: 'newtable' },
        on: context.data.attrs.config.listeners,
        attrs: { ref: 'newtable' },
        scopedSlots
      },
      context.data.attrs.config.children
    )
  }
})

针对函数式组件没有实例的问题,这里我直接调用了父级组件的$createElement方法去创建el-table,再利用父级组件的children中‘children中`children中‘options._componentTag === 'el-table'`的vnode,来拿到对应的实例

有点奇怪的是我在创建的时候给生成的组件配置attrs的ref,在父组件中$refs无法拿到

还有一点要注意!我在控制组件重新渲染的时候,使用了$nexttick,所以不要在钩子函数中使用getInstance()方法获取表格组件实例,如果一定要,那就用链式判一下空再用this.$refs.hftable.getInstance()?.xxx()

后话

其实这是不是的合适方案也未定,但是最主要是过程中的一个探索吧。包括评论说的$slots去继承所有的vnode节点。其实在拿不到表格实例的时候我想换方案的时候尝试过这个办法,我直接去掉了函数式组件。直接写了一个el-table,然后具名插槽去接收。但是因为要操作vnode,也就是我要隐藏某列,这意味着我需要去修改hf-table中$slots中的default数组,就总觉得不太合适,共同进步吧~更多关于表格函数式组件封装element的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue+Element UI实现下拉菜单的封装

    本文实例为大家分享了Vue+Element UI实现下拉菜单封装的具体代码,供大家参考,具体内容如下 1.效果图 先贴个效果图,菜单项没有做样式美化,图中显示的边框也是没有的(边框是外部容器的边框),其它的根据需要自己修改一下样式即可. 2.组件封装 组件的封装用到了CSS动画.定位.,以及Element UI提供的下拉菜单组件el-dropdown.代码如下, <template> <div class="all" @click="clickFire&qu

  • Vue Element前端应用开发之前端API接口的封装

    1.ABP框架API接口的回顾 ABP是ASP.NET Boilerplate的简称,ABP是一个开源且文档友好的应用程序框架.ABP不仅仅是一个框架,它还提供了一个最徍实践的基于领域驱动设计(DDD)的体系结构模型. 启动Host的项目,我们可以看到Swagger的管理界面如下所示. 上图就是ABP后端框架的API接口的查看页面,从上图可以看到,一般业务对象,都有Get.GetAll.Create.Update.Delete等常见接口,由于这些接口是给前端进行调用的. Vue + Elemen

  • Vue3+ElementPlus 表单组件的封装实例

    目录 form文件夹 FormItem.tsx 在页面中引用 总结 在系统中,表单作为用户与后端交互的重要传递组件使用频率极高,故对其进行封装是必然的,也是一个编写规范代码的前端程序员必须做的一件事. 在Vue3中封装组件时,能感受到与Vue2有着很大的不同,故作此记录. form文件夹 FormItem.tsx文件是Typescript中的新特性之一,详细可查阅TS中文文档 index.vue是主体文件 type.ts表单的规约 FormItem.tsx import filter from

  • vue-cli对element-ui组件进行二次封装的实战记录

    目录 为什么要element对进行二次封装? 如何对element对进行二次封装? 总结 为什么要element对进行二次封装? 1.element-ui组件的部分样式不满足当前项目的需求. element-ui组件的样式是固定的,比如我们常用的那些组件,table,button,icon,tab等等.当我们需要的样式和element组件有偏差的时候,我们可以通过针对element组件进行二次封装,然后通过Vue.component()方法,定义到全局,来解决我们当前的项目需求. 2.eleme

  • 如何封装Vue Element的table表格组件

    在封装Vue组件时,我依旧会交叉使用函数式组件的方式来实现.关于函数式组件,我们可以把它想像成组件里的一个函数,入参是渲染上下文(render context),返回值是渲染好的HTML(VNode).它比较适用于外层组件仅仅是对内层组件的一次逻辑封装,而渲染的模板结构变化扩展不多的情况,且它一定是无状态.无实例的,无状态就意味着它没有created.mounted.updated等Vue的生命周期函数,无实例就意味着它没有响应式数据data和this上下文. 我们先来一个简单的Vue函数式组件

  • Vue3+Vite+TS实现二次封装element-plus业务组件sfasga

    目录 1.结构字符串 2.返回tuple元组 3.访问Dict字典 4.运用库 5.在列表中切片/步进 6.用 ranges 1.结构字符串 你会经常需求打印字符串.要是有很多变量,防止下面这样: name = "Raymond" age = 22 born_in = "Oakland, CA" string = "Hello my name is " + name + "and I'm " + str(age) + &quo

  • 函数式组件劫持替代json封装element表格

    目录 背景 思路 想法与实践 el-table-column获取 如何渲染表格 操作表格 组件代码 表格函数式组件 问题点与优化 hf-table.vue table.js 后话 背景 系统有个整改需求,要求系统内的所有表格支持本地动态列显隐,拖拽排序列位置,固定列功能,涉及的页面很多 上效果图: 思路 其实最开始想的肯定是json配置表单的形式,再由循环出来的列去控制对应的位置和属性 但是!很多页面啊!每个页面都要去转json配置意味着大量的工作量和极高的风险 能不能我就写个自己的组件来包一层

  • 基于Element封装一个表格组件tableList的使用方法

    我们项目中使用的表格一般都比较类似,如果不进行封装的话,那么每个页面都可能有一些类似的代码.不仅浪费时间,而且由于开发人员不同的开发习惯.后期维护人员需要花费一点时间去看每个人的代码.所以我直接将表格做一个二次封装,只要一个人去维护这份代码即可.下面是我封装的内容 内容: 1.支持直接传入后台请求地址渲染列表,且参数修改之后自动刷新 2.支持自定义每一列的显示 3.支持根据内容自动撑开列宽 4.支持动态筛选表头 5.支持分页 6.防抖 7.列权限 ... 更多请自行尝试 以下是tableList

  • Vue业务组件封装Table表格示例详解

    目录 前言 Table组件介绍 Table组件封装思路 了解element Table组件代码 Table组件如何去封装 新建LTable组件 配置文件 配置插槽 动态组件 解决插槽存在的问题 代码实现 总结 前言 这个系列主要是分享自己在工作中常用到的业务组件,以及如何对这些组件进行有效的封装和封装的思路.注:都是基于element ui进行二次封装. 封装组件的基本方法就是通过props和emit进行父子组件的传值和通信.利用插槽.组件等去增加组件的可扩展性和复用性. Table组件介绍 用

  • element表格组件实现右键菜单的功能

    目录 前言 实现思路 最后 前言 最近产品给我提了个需求 ————想要实现用户右键table的某一行时,显示该行操作栏的功能.觉得这个需求挺有意思的,特此分享给大家. 技术栈: elementUI 实现思路 要实现右键菜单 我们需要定义一个菜单栏组件 当用户点击table的某一行时,我们处理好显示位置再将菜单栏渲染到table上.先实现这个rightKeyMenu组件: // right-key-menu.vue <template> <div id="right-key-me

  • Vue3 封装 Element Plus Menu 无限级菜单组件功能的详细代码

    目录 1 数据结构定义 1.1 菜单项数据结构 1.2 菜单配置数据结构 2 使用 tsx 实现封装 2.1 tsx 基本结构 2.2 定义 prop 2.3 递归实现组件 3 使用 SFC 实现菜单封装 3.1 封装菜单项的渲染 3.2 封装菜单组件 4 测试组件 4.1 菜单测试数据 4.2 测试页面 4.3 运行效果 总结: 本文分别使用 SFC(模板方式)和 tsx 方式对 Element Plus el-menu 组件进行二次封装,实现配置化的菜单,有了配置化的菜单,后续便可以根据路由

  • Vue.js函数式组件的全面了解

    目录 前言 React 函数式组件 Vue(2.x) 中的函数式组件

  • 使用 TypeScript 开发 React 函数式组件

    目录 前言 如何使用 TypeScript 定义函数式组件 1. 使用 React.FC 2. 使用 JSX.Element 3. 直接定义完整类型 4. 使用 React.PropsWithChildren 使用过程需要注意的点 1. 函数式组件返回值不能是布尔值 2. 无法为组件使用 Array.fill() 填充 3. 支持使用泛型来创建组件 前言 在我们使用 React 开发项目时,使用最多的应该都是组件,组件又分为函数组件和类组件,我们可以这么定义: 定义函数组件: function

  • vue element表格某一列内容过多,超出省略号显示的实现

    目录 element表格某一列内容过多,超出省略号显示 这样就好了,效果如下 element-UI table文字超出两行,隐藏多余文字,移入显示tips element-UI表格的列属性 超出两行隐藏多余文本,移入时tips显示全部内容 超出的文本的隐藏 文本超过两行,移入时tips显示全部内容 通过长度判断 element表格某一列内容过多,超出省略号显示 在使用element组件库里面的table组件时,遇到某一个字段内容过多,导致td高度被撑开,布局显得很不美观,像这样 这时我们只要给t

  • vue封装动态表格方式详解

    目录 前言 实现方式简述 表格实现: func组件 text组件: 调用示例 效果 前言 这里只是提供一种想法并提供一些快速实现,实际这些技巧可以用在很多地方,如:动态表单 实现方式简述 通过json定义要显示的列 通过slot实现自定义列 通过require.context实现组件的自动注册,并通过<components is="xxx"></components>, 调用动态注册的组件 优点: 支持通过slot自定义扩展 支持编写vue文件扩展列的显示方式

  • 浅谈VUE防抖与节流的最佳解决方案(函数式组件)

    前言 有echarts使用经验的同学可能遇到过这样的场景,在window.onresize事件回调里触发echartsBox.resize()方法来达到重绘的目的,resize事件是连续触发的这意味着echarts实例会连续的重绘这是非常耗性能的.还有一个常见的场景在input标签的input事件里请求后端接口,input事件也是连续触发的,假设我输入了"12"就会请求两次接口参数分别是"1"和"12",比浪费网络资源更要命的是如果参数为&quo

随机推荐