el-tree的初次使用总结

本文档展示了如何使用Vue.js创建一个包含树形菜单和关键词筛选功能的组件。通过处理服务器返回的数据,将其转换为适用于el-tree组件的格式,并实现了节点点击事件和关键词筛选功能。同时,提供了样式调整以优化用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

没啥说的,一切尽在代码中

我的效果图
在这里插入图片描述

单页面代码:(这里在catch中定义了api返回的测试数据,可直接运行看效果!)
功能说明:(展示tree树菜单、关键字筛选)这里的tree数据格式:
在这里插入图片描述

<template>
    <el-container style="
      min-height: 0px;
      max-height: 100%;
      border-right: 1px solid #eeeeee;
      border-left: 1px solid #eeeeee;
      border-bottom: 1px solid #eeeeee;
    ">
        <!--条件筛选的输入框  @input:输入框值改变时触发"-->
        <div style="margin-left: 6px;">
            <el-input style="margin-top: 8px;" placeholder="请输入关键字..." v-model="filterText" clearable></el-input>
            <!--el-aside侧边栏-->
            <el-aside width="300px"
                      style="background-color: #eeeeee;height: 400px;padding: 5px; border-right: 1px solid #eeeeee;margin-top: 6px;">
                <!--el-tree树形菜单  data:数据源 | props:映射 | @node-click:选中后触发 | :filter-node-method:过滤的方法 | ref:定义变量,在方法中获取tree值-->
                <el-tree :data="departmentArray" :props="defaultProps" @node-click="handleNodeClick"
                         :filter-node-method="filterNode" ref="tree"></el-tree>
            </el-aside>
        </div>


    </el-container>
</template>
<script>
    import Vue from '@/main'
    export default {
        name: "update-department.component",
        title:'岗位/任岗',
        data() {
            return {
                //input 关键字
                filterText: '',
                //el-tree 数据展示的映射字段
                defaultProps: {
                    children: 'children',
                    label: 'nameCn',//filterNode 方法筛选方法时,字段使用nameCn
                },
                //el-tree的数据集合(部门 一级岗位?二级岗位?)
                departmentArray: [],


            };
        },

        mounted() {
            this.queryDepartmentJob();
        },

        watch: {
            //输入框值发生变化~触发 | 配合tree的过滤数据filterNode方法使用
            filterText(val) {
                this.$refs.tree.filter(val);
            },

        },

        methods: {
            //查询树形菜单| 部门信息即岗位列表
            queryDepartmentJob() {
                Vue.$Request({
                    method: 'post',
                    url: '/post/list',
                    data: {},
                    params: {}
                }).then((res) => {
                  if (res.data.code === 200) {
                        console.log("tree数据:", JSON.stringify(res.data.data.dataList));
                        //tree下展示岗位的层级关系,并将岗位放到所属的部门下;
                        //departmentName departmentId 部门   主管岗位?
                        //nameCn  岗位 | pid为空则为一级岗位 ,否则为二级岗位
                      this.departmentArray = this.format(res.data.data.dataList);
                    }
                }).catch(error => {
                    let res=[{"id":10047,"opCode":null,"useStatus":1,"departmentId":30758,"nameCn":"测试数据1124","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":0,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":"HBLX01_SYS","pId":null,"createTime":"2021-11-24 14:39:47","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"数字化管理中心"},{"id":10011,"opCode":null,"useStatus":1,"departmentId":30754,"nameCn":"电商部长","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":0,"postType":null,"effectiveDate":"2001-01-01 00:00:00","expiryDate":"2001-01-01 00:00:00","memo":"无","reportType":null,"pId":null,"createTime":"2021-11-24 14:37:05","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"人力资源中心"},{"id":10010,"opCode":null,"useStatus":1,"departmentId":30754,"nameCn":"信息主管","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":0,"postType":null,"effectiveDate":"2001-01-01 00:00:00","expiryDate":"2001-01-01 00:00:00","memo":null,"reportType":null,"pId":null,"createTime":"2021-11-24 14:37:03","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"人力资源中心"},{"id":10006,"opCode":null,"useStatus":1,"departmentId":30754,"nameCn":"行政管理","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":null,"createTime":"2021-11-24 14:37:01","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"人力资源中心"},{"id":10005,"opCode":null,"useStatus":1,"departmentId":30754,"nameCn":"电商客服专员","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":null,"createTime":"2021-11-24 14:37:00","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"人力资源中心"},{"id":10004,"opCode":null,"useStatus":1,"departmentId":30758,"nameCn":"信息专员","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":10000,"createTime":"2021-11-16 11:13:46","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"数字化管理中心"},{"id":10003,"opCode":null,"useStatus":1,"departmentId":30758,"nameCn":"软件测试","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":10000,"createTime":"2021-11-16 11:13:44","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"数字化管理中心"},{"id":10002,"opCode":null,"useStatus":1,"departmentId":30758,"nameCn":"软件开发","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":10000,"createTime":"2021-11-16 11:13:43","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"数字化管理中心"},{"id":10001,"opCode":null,"useStatus":1,"departmentId":30758,"nameCn":"软件架构","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":10000,"createTime":"2021-11-16 11:13:41","createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"数字化管理中心"},{"id":10000,"opCode":null,"useStatus":1,"departmentId":30758,"nameCn":"信息总监","nameEn":null,"pinyinSimple":null,"pinyin":null,"director":1,"postType":null,"effectiveDate":null,"expiryDate":null,"memo":null,"reportType":null,"pId":null,"createTime":null,"createUserId":null,"updateTime":null,"updateUserId":null,"other":null,"field1":null,"field2":null,"field3":null,"departmentName":"数字化管理中心"}];
           
                    // console.log("tree数据:", JSON.stringify(this.departmentArray));
                    this.departmentArray = this.format(res);
                    this.$message.warning('服务器抛出异常信息:' + error);
                })
            },

            //第一步:遍历原始数组~提炼部门信息(名称、ID)到新数组(部门数组)
            format(array) {
                //定义部门数组
                let arr = [];
                array.forEach(obj => {
                    //这里之所以将部门名称字段,重新取名nameCn,是因为tree数据渲染,必须是根据某一个字段(因为岗位名称字段:nameCn,保持一致)进行嵌套展示!
                    arr.push({departmentId: obj.departmentId, nameCn: obj.departmentName});
                })
                console.log("提炼出的部门数组:", JSON.stringify(arr));//有重复的部门数组

                //部门数组去重(去除重复部门)  | 利用对象访问属性的方法,判断对象中是否存在重复的departmentId
                let deparement_array = [];
                let obj = {};
                for (let i = 0; i < arr.length; i++) {
                    if (!obj[arr[i].departmentId]) {
                        deparement_array.push(arr[i]);
                        obj[arr[i].departmentId] = true;
                    }
                }
                console.log("去重后的部门数组:", JSON.stringify(deparement_array));

                return this.setTreeData(array, deparement_array);
            },

            //第二步:实现岗位和子岗位层级关系
            setTreeData(arr, arr1) {
                // 删除所有的children,以防止多次调用
                arr.forEach(function (item) {
                    delete item.children;
                });
                let map = {}; //构建map
                arr.forEach(i => {
                    map[i.id] = i; //构建以id为键 当前数据为值
                });
                let treeData = [];
                arr.forEach(child => {
                    const mapItem = map[child.pId]; //判断当前数据的pId是否存在map中
                    if (mapItem) {
                        //存在则表示当前数据不是最顶层的数据
                        //注意: 这里的map中的数据是引用了arr的它的指向还是arr,当mapItem改变时arr也会改变,踩坑点
                        (mapItem.children || (mapItem.children = [])).push(child); //这里判断mapItem中是否存在child
                    } else {
                        //不存在则是顶层数据
                        treeData.push(child);
                    }
                });
                console.log("封装好的岗位数组:", JSON.stringify(treeData));


                //最后一步:合并部门和岗位的层级关系(将岗位层级放到所属部门下children数组下)
                for (let a = 0; a < treeData.length; a++) { // 封装好的岗位层级的数组
                    for (let b = 0; b < arr1.length; b++) {//父 部门数组
                        if (treeData[a].departmentId === arr1[b].departmentId) {
                            (arr1[b].children = []).push(treeData[a]);//1.arr1添加一个属性,并声明是数组 2.往数组中添加对象
                        }
                    }
                }
                console.log("终于得到符合el-tree的数据格式:", JSON.stringify(arr1));
                return arr1;
            },

            //树形菜单筛选数据方法,配合input的(watch中)filterText一起使用。
            filterNode(value, data) {
                if (!value) return true;
                return data.nameCn.indexOf(value) !== -1;
            },

            //当前el-tree选中项(只要将服务端数据封装成el-tree想要的格式,这里方法可以直接拿到点击的对象!)
            handleNodeClick(data) {
                console.log(data);
            },

        },

    };
</script>
<style>
    .el-tree {
        color: #000;
    }

    .el-menu--horizontal > .el-menu-item.is-active {
        /* 顶部菜单项选中时的颜色 */
        border-bottom: 2px solid #009944;
        /* 顶部菜单栏底线颜色 */
        color: #303133;
    }

    .el-tree-node__expand-icon {
        /* 树形菜单三角标颜色 */
        color: #999;
    }

    .el-tree-node__content:hover {
        /* 树形菜单鼠标悬浮的字体颜色及背景颜色 */
        color: #ffffff;
        background-color: #009944;
    }

    .el-tree-node:focus > .el-tree-node__content {
        /* 树形菜单选中时的字体颜色及背景颜色 */
        color: #ffffff;
        background-color: #009944;
    }

    .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
        /* 树形菜单选中时的字体颜色及背景颜色 */
        color: #ffffff;
        background-color: #009944;
    }

    .el-button__content:hover {
        color: #ffffff;
        background-color: #009944;
    }

    .el-scrollbar__wrap {
        margin-bottom: 0px !important;
    }

    /**根据ID或员工姓名查询时,显示的侧边栏样式**/
    .sidebar-left {
        color: #7a7a7a;
        /*background-color: #3a8ee6;*/
        height: 1200px;
        overflow: auto;
    }

    .sidebar-left-content {
        padding: 6px 12px;
        margin: 6px 0;
        cursor: pointer;
        font-size: 14px;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    .sidebar-left-content2 {
        background-color: #009944;
        color: white;
    }

    .sidebar-left-content:hover {
        color: #009944;
        font-size: 14.5px;
        background-color: #eeeeee;
    }

    .condition {
        background-color: #eeeeee;
        padding: 8px 28px;
        /*color: #8c8c8c;*/
        cursor: pointer;
        font-size: 14px;
    }

    /**点击后改变背景色和文字颜色**/
    .click_sty {
        background-color: #009944;
        color: white;
    }

    @media screen and (min-height: 0px) and (max-height: 749px) {
        #edit {
            overflow: auto;
            height: 500px;
        }
    }

    @media screen and (min-height: 750px) and (max-height: 10900px) {
        #edit {
            overflow: auto;
            height: 700px;
        }
    }

</style>

后语:

虽然是一个不是很难的功能,但是对于我来说花费不少时间去实现,主要就是初期不知道如何将服务器返回的JSON数据,转换成el-tree的数据格式,最后实现功能后,深感自己JavaScript知识薄弱。

<think>好的,我现在需要解决用户提到的el-tree-select在dialog中性能较差的问题。首先,我得先理解这个问题的背景和可能的原因。用户提到当el-tree-select组件放在dialog中时,响应速度慢,这可能涉及到组件渲染、数据加载、事件处理等多个方面。 首先,我想到的是Element UI的组件在对话框中的渲染机制。通常,对话框在初次打开时才会渲染其内容,如果el-tree-select的数据量很大,比如有成千上万的节点,那么初始化渲染可能会很耗时。特别是如果每次打开对话框都重新加载数据,或者节点结构复杂,可能会导致明显的延迟。 接下来,我需要考虑数据加载的方式。用户是否在每次打开dialog时都重新请求数据?如果是的话,频繁的数据请求加上大量的节点渲染,肯定会影响性能。这时候,可能需要将数据缓存起来,只在第一次加载时获取,之后复用已有的数据。 另一个方面是el-tree-select的配置选项。比如,是否开启了懒加载(lazy),如果没有,而数据量又大,一次性渲染所有节点肯定会慢。懒加载可以按需加载子节点,减少初始渲染的压力。此外,虚拟滚动(virtual-scrolling)也是一个优化点,但Element UI的el-tree-select可能不支持,这时候可能需要寻找替代方案或自行实现类似功能。 节点的展开和勾选事件也可能影响性能。如果绑定了过多的事件处理函数,或者这些函数执行了复杂的操作,可能会拖慢响应速度。检查是否有不必要的事件监听,或者优化事件处理函数的逻辑,比如使用防抖或节流技术。 另外,对话框的销毁和重建也是一个因素。如果对话框在关闭时被销毁,再次打开时需要重新创建组件并渲染,这会导致延迟。可以尝试将对话框设置为不销毁,通过v-if或keep-alive来保持组件状态,减少重复渲染的开销。 组件的版本和兼容性问题也不能忽视。用户可能使用的是较旧的Element UI版本,存在性能方面的bug。建议检查是否有新版本,并查看更新日志中是否有相关的性能优化。 最后,如果以上优化措施仍然不足,可能需要考虑替代方案,比如分页加载数据、使用el-select配合el-tree的独立组件,或者采用第三方优化过的树形选择组件。 总结下来,可能的优化步骤包括:减少初始数据量、启用懒加载、优化事件处理、缓存数据、避免重复渲染、组件保持存活、检查版本更新,以及考虑替代方案。需要逐一排查这些方面,找到具体的瓶颈所在,然后针对性地进行优化。</think>### el-tree-select 在 dialog 中的性能优化方法 #### 一、性能瓶颈分析 1. **渲染层级问题** el-tree-select 在 dialog 中会生成多层嵌套的 DOM 结构,dialog 本身已是独立渲染层,两者叠加可能导致浏览器重绘/回流效率降低[^3]。 2. **数据加载方式** 若树形数据未做懒加载(lazy),一次性渲染大体积数据(如超过 1000 个节点)会显著拖慢初始化速度[^1]。 3. **组件生命周期** dialog 默认通过 `v-if` 控制销毁与重建,每次打开都会重新挂载 el-tree-select 并触发全量计算[^3]。 --- #### 二、优化方案及实施步骤 ##### 1. 数据加载优化 - **启用懒加载** 对 el-tree 配置 `lazy` 属性并按需加载子节点: ```html <el-tree-select :props="{ lazy: true, load: loadNode }" :lazy="true" ></el-tree-select> ``` 通过 `loadNode` 方法实现分批次加载数据。 - **数据缓存策略** 使用 Vuex 或 localStorage 缓存已加载的树形数据: ```javascript // 首次加载后缓存 localStorage.setItem('cachedTreeData', JSON.stringify(treeData)); ``` ##### 2. 渲染性能优化 - **虚拟滚动支持** 自定义虚拟滚动容器(Element UI 原生未支持): ```html <el-tree-select> <virtual-list :size="40" :remain="8"></virtual-list> </el-tree-select> ``` 推荐使用第三方库如 `vue-virtual-scroller`[^2]。 - **节点简化渲染** 通过 `render-content` 自定义精简节点内容: ```javascript renderContent(h, { node }) { return h('span', { class: 'compact-node' }, node.label); } ``` ##### 3. 交互优化 - **延迟加载策略** 在 dialog 打开后延迟 200ms 再初始化树组件: ```javascript this.showDialog = true; setTimeout(() => { this.initTree() }, 200); ``` - **事件防抖处理** 对搜索功能添加防抖限制(建议 300ms): ```javascript watch: { filterText: _.debounce(function(val) { this.tree.filter(val); }, 300) } ``` ##### 4. 组件状态保持 - **保持组件存活** 修改 dialog 的 `destroy-on-close` 为 `false`: ```html <el-dialog :destroy-on-close="false"></el-dialog> ``` 或使用 `<keep-alive>` 包裹组件。 --- #### 三、验证指标 | 优化措施 | 预期提升幅度 | 验证方法 | |---------|-------------|---------| | 懒加载 | 首屏加载速度提升 60% | Chrome DevTools 的 Performance 面板 | | 虚拟滚动 | 滚动流畅度提升 80% | 滚动 FPS 检测 | | 组件保活 | 二次打开速度提升 90% | 内存占用量对比 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值