如何在Vue2项目中使用ElementPlus虚拟树

2,885 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

背景

在ztree不满足现有的业务背景下,得知有虚拟节点概念,且主流elementPlus、ant等均封装了虚拟树、虚拟列表、虚拟表格等,并做相关的调研,虚拟树可以解决目前使用中的节点性能问题,但是目前还存在一些问题,要等一段时间在进行推广

问题

项目使用的技术栈vue2+elementUI,想使用elementPlus的虚拟树组件

思路

  1. 参考elementPlus tree-V2的源码使用vue2进行改写实现

    不太会vue3、typescript 时间成本比较大

  2. 是否可以elementUI、elementPlus共存在同一项目使用

    经过尝试该方式不行

  3. 直接使用vue3+vite+elementPlus(tree-V2)封装成组件包,在vue2中安装使用

    试验后可走通

    🤦‍♀️ 个人能力、时间有限,没考虑过从0-1

实现

把vue3+elementPlus(tree-V2)组件一起打包,通过创建挂载实例的方式,把vue3组件挂载在vue2项目的dom节点上。从而实现在vue2中使用vue3的组件

  1. 新建vue3+vite+elementPlus项目,通过把vue3+elementPlus(el-tree-v2)一起打包成压缩文件VirtualTree.umd.js,对外抛出一个函数,接收2个参数el、prop,返回值是组件树应用实例。el:挂载的dom节点,prop:根组件的 attributes属性 和事件监听器。
// packages/index.js 核心文件
import 'element-plus/theme-chalk/el-tree.css';
import { ElTreeV2 } from "element-plus";
import { createApp} from "vue";

export  default function(el, props) {
	// 参数说明 插入节点的el、props(所有的配置项、属性等,)、方法、事件
	return createApp(ElTreeV2,props).mount(el)
}
// vite.config.ts 打包配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      vue: 'vue/dist/vue.esm-bundler.js',
      "@": path.resolve(__dirname, "src")
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "./src/assets/style/index.scss";'
      }
    }
  },
  build: {
    lib: {
      entry: path.resolve(__dirname, 'packages/index.js'),
      name: 'CetVirtualTree',
      fileName: (format) => `CetVirtualTree.${format}.js`
    },
  }
})
  1. 在vue2项目中,引入VirtualTree.umd.js,创建dom节点,调用createTreeV2并传入el、prop参数。使用变量保存返回实例,且会触发onCreate事件,外部可获取组件树实例。 elementPlus组件事件通过事件转发($emit)方式实现,方法通过实例去调用。并使用 npm包的方式去封装发布@virtualtree
<template>
  <div class="virtual-tree"></div>
</template>
<script>
// vue3+elementPlus+vite 打包生成物
import createTreeV2 from "./lib/VirtualTree.umd.js";
const treeInstance = Symbol('virtualTree');

const EVENTS = [
  "onNodeClick",
  "onNodeContextmenu",
  "onCheckChange",
  "onCheck",
  "onCurrentChange",
  "onNodeExpand",
  "onNodeCollapse"
];

export default {
  name: "VirtualTree",
  props: {
    attribute: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  data() {
    return {};
  },
  watch: {},
  mounted() {
    this.init();
  },
  methods: {
   init() {
      //使用$emit实现事件转发
      let event = this.forwardingEvents();
      let props = Object.assign(this.attribute, event);
      // 创建并挂载,返回实例
      this[treeInstance] = createTreeV2(this.$el, props);
      // 外部通过事件获取实例调用方法等
      this.$emit("onCreate", this[treeInstance]);
    },
    // elementplus事件转发,方法直接通过 this[CetTreeV2]获取
    forwardingEvents() {
      const vm = this;
      let obj = {
        // onCheck:this.onCheck
      };
      EVENTS.forEach(eventName => {
        vm[eventName] = (...args) => {
          vm.$emit(eventName, ...args);
        };
        obj[eventName] = vm[eventName];
      });
      return obj;
    }
  }
};

</script>

3.打包发布

image.png

  1. 全局安装引用@virtualtree 包,通过VirtualTree 名称来使用。(图三)
//main.js
import VirtualTree from "@virtualtree";
Vue.use(VirtualTree);
<template>
    <div>
      <el-input v-model="filterText" placeholder="Filter keyword" />
      <VirtualTree
          v-bind="VirtualTree_obj"
          v-on="VirtualTree_obj.event"
        ></VirtualTree>
      </div>  
 </template>

export default{
data() {
VirtualTree_obj: {
    attribute: {
      data: [], 
      props: {
        value: "id",
        label: "name",
        children: "children"
      },
     },
    event: {
      onCreate: this.onCreate,
      onCheck: this.onCheck,
     }
    }
  }