【无标题】实战:Vue3 + Element Plus 实现树形选择器全量预加载与层级控制

在后台管理系统开发中,树形选择器(TreeSelect)是高频使用的组件,Element Plus 提供的 el-tree-select 虽然支持懒加载,但默认需要点击节点才会加载子层级数据。本文将分享如何基于 Vue3 + Element Plus 实现页面初始化时全量预加载树形数据,并结合业务场景实现「指定层级停止加载」的优化方案,解决频繁接口请求、用户体验差的问题。

一、业务场景与痛点

1. 核心需求

  • 行政 / 部门树形选择器,层级固定(如省 / 市 / 区三级),三级节点无需加载子节点;
  • 页面初始化时自动加载所有层级数据,避免用户点击节点时等待接口响应;
  • 保留树形选择器的过滤、选中功能,基于本地数据交互,提升体验。

2. 传统懒加载的痛点

  • 每次点击节点都触发接口请求,网络延迟时用户需等待;
  • 多次重复请求同一节点数据,增加后端压力;
  • 过滤功能需基于已加载的节点,未加载的子节点无法被过滤。

二、技术方案设计

1. 核心思路

  • 递归预加载:页面挂载后先加载根节点,再递归遍历所有节点加载子节点,直到指定层级(三级)停止;
  • 缓存机制:通过 Set 缓存已加载的节点 ID,避免重复请求;
  • 异常处理:请求失败时兜底赋值空数组,防止组件渲染异常;
  • 加载状态:添加加载提示,避免页面空白导致用户误解。

2. 技术栈

  • 框架:Vue3(Setup 语法糖)
  • UI 组件:Element Plus el-tree-select
  • 接口请求:Axios(封装后的业务接口)
  • 核心特性:异步递归、响应式数据、Set 缓存

三、完整实现代码

1. 模板部分(Template)

复制代码
<template>
  <div class="container">
    <!-- 加载状态提示:无动画纯文本,适配极简风格 -->
    <div v-if="loading" class="loading-text">加载中...</div>
    <!-- 树形选择器:基于预加载的本地数据渲染 -->
    <el-tree-select
      v-else
      v-model="selectedValue"
      :data="treeData"
      :filter-node-method="filterNodeMethod"
      filterable
      placeholder="请选择部门/区域"
      style="width: 240px"
    />
  </div>
</template>

2. 脚本部分(Script Setup)

复制代码
import { ref, onMounted } from 'vue'
// 业务接口:加载指定节点的子层级数据(根节点不传参)
import { deptTreeJustOneLevel } from "@/api/login"

// 响应式数据定义
const selectedValue = ref('') // 选中值
const treeData = ref([]) // 树形选择器数据源
const loading = ref(false) // 加载状态
const loadedNodeIds = ref(new Set()) // 已加载节点ID缓存,避免重复请求

/**
 * 递归预加载所有子节点
 * @param {Array} nodes 当前层级的节点列表
 * @description 遍历节点加载子数据,三级节点(departmentLevel === "3")停止加载
 */
const preloadAllChildren = async (nodes) => {
  // 遍历当前层级所有节点
  for (const node of nodes) {
    // 核心:三级节点不加载子节点,直接跳过
    if (node.departmentLevel === "3") {
      continue
    }
    // 缓存命中:已加载的节点不再请求
    if (loadedNodeIds.value.has(node.value)) {
      continue
    }

    try {
      // 调用接口加载子节点(传当前节点ID)
      const childrenRes = await deptTreeJustOneLevel({ deptId: node.value })
      const children = childrenRes.data
      // 缓存已加载节点ID
      loadedNodeIds.value.add(node.value)
      // 给当前节点赋值子节点,供组件渲染
      node.children = children
      // 递归加载子节点的子节点(非三级节点才继续)
      if (children.length > 0 && node.departmentLevel !== "3") {
        await preloadAllChildren(children)
      }
    } catch (err) {
      console.error(`加载节点【${node.label}】的子数据失败:`, err)
      // 异常兜底:赋值空数组,防止树形组件渲染报错
      node.children = []
    }
  }
}

/**
 * 初始化全量预加载数据
 * @description 页面挂载后执行,先加载根节点,再递归加载所有子节点
 */
const initPreloadAllData = async () => {
  loading.value = true
  try {
    // 1. 加载根节点(不传参)
    const rootRes = await deptTreeJustOneLevel()
    const rootNodes = rootRes.data
    // 2. 递归加载所有子节点
    await preloadAllChildren(rootNodes)
    // 3. 赋值给树形选择器数据源
    treeData.value = rootNodes
    console.log('所有层级数据初始化完成:', treeData.value)
  } catch (err) {
    console.error('初始化加载树形数据失败:', err)
    treeData.value = []
  } finally {
    // 无论成功/失败,关闭加载状态
    loading.value = false
  }
}

/**
 * 过滤节点方法(可选)
 * @param {String} value 过滤关键词
 * @param {Object} data 节点数据
 * @returns {Boolean} 是否匹配
 */
const filterNodeMethod = (value, data) => {
  if (!value) return true
  return data.label.toLowerCase().includes(value.toLowerCase())
}

// 页面挂载后执行初始化
onMounted(() => {
  initPreloadAllData()
})

3. 样式部分(Style Scoped)

复制代码
.container {
  padding: 20px;
}

/* 加载文本样式:与组件高度一致,避免页面跳动 */
.loading-text {
  line-height: 32px; /* 匹配el-tree-select高度 */
  text-align: center;
  color: #666;
  font-size: 14px;
  width: 240px;
}

四、核心逻辑解析

1. 递归预加载(preloadAllChildren)

这是整个方案的核心,实现步骤:

  1. 遍历当前层级的所有节点,判断是否为三级节点,若是则跳过;
  2. 检查节点 ID 是否在缓存中,避免重复请求;
  3. 调用接口加载子节点,赋值给当前节点的 children 属性;
  4. 递归处理子节点,直到所有层级加载完成或遇到三级节点。

2. 缓存机制(loadedNodeIds)

使用 ES6 的 Set 结构缓存已加载的节点 ID,相比数组:

  • 查找效率更高(O (1)),适合高频判断;
  • 自动去重,无需手动处理重复 ID。

3. 异常处理

  • 接口请求失败时,给节点赋值空数组 node.children = [],防止树形组件因 children 为 undefined 报错;
  • 根节点加载失败时,treeData 赋值空数组,保证页面不崩溃。

4. 加载状态管理

  • 初始化时开启 loading,请求完成(无论成败)关闭;
  • 加载文本的 line-height 与组件高度一致,避免页面布局跳动。

五、总结与思考

本文实现的「树形选择器全量预加载」方案,核心是通过异步递归将懒加载的接口请求提前到页面初始化阶段,结合缓存机制避免重复请求,既解决了传统懒加载的体验问题,又保证了数据的实时性(可通过刷新方法更新)。

适用场景

  • 树形数据层级固定、数据量适中(千级以内);
  • 对用户体验要求高,不希望点击节点时等待;
  • 需要基于全量数据实现过滤、检索功能。

注意事项

  • 若树形数据量极大(万级以上),不建议全量预加载,可结合「虚拟滚动 + 懒加载」优化;
  • 需与后端确认接口频率限制,避免初始化时请求过多导致接口限流;
  • 缓存数据需考虑时效性,可添加缓存过期时间或手动刷新机制。

通过该方案,既能保留 Element Plus 树形选择器的原生功能,又能基于业务场景优化交互体验,是后台管理系统中树形选择组件的一种高效实现方式。

编辑分享

相关推荐
晚烛2 小时前
实战前瞻:构建高可靠、低延迟的 Flutter + OpenHarmony 智慧交通出行平台
前端·javascript·flutter
WHOVENLY2 小时前
【javaScript】- 作用域[[scope]]
前端·javascript
来杯三花豆奶2 小时前
Vue3 Pinia 从入门到精通
前端·javascript·vue.js
syt_10132 小时前
设计模式之-工厂模式
javascript·单例模式·设计模式
卡布叻_星星2 小时前
Docker之Nginx前端部署(Windows版-x86_64(AMD64)-离线)
前端·windows·nginx
LYFlied2 小时前
【算法解题模板】-解二叉树相关算法题的技巧
前端·数据结构·算法·leetcode
weibkreuz2 小时前
React的基本使用@2
前端·javascript·react.js
于是我说3 小时前
前端JavaScript 项目中 获取当前页面滚动位置
开发语言·前端·javascript
小肖爱笑不爱笑3 小时前
JavaScript
java·javascript·json·web