Vue Element Admin实战:从搭建到优化的完整开发经验分享

子儒(打工版) 框架 阅读 1,077
赞 16 收藏
二维码
手机扫码查看
反馈

为什么选 Vue Element Admin?

去年接了个内部管理系统,需求急、人手少,老板说“两周上线第一版”。我翻了翻手头的模板,Vue Element Admin(VEA)成了最省事的选择——开箱即用的权限控制、现成的布局、Element UI 组件全集成。虽然知道它有点重,但项目初期谁管这些?能跑就行。

Vue Element Admin实战:从搭建到优化的完整开发经验分享

事实证明,对于中小型后台系统,VEA 确实能快速搭出个像样的架子。路由配置、登录拦截、多级菜单,这些基础功能几乎不用动。但问题也很快来了——越往后做,越觉得它“太聪明”,反而绑住了手脚。

最大的坑:动态路由和权限控制的耦合

VEA 默认把路由和权限写死在 src/router/index.js 里,通过 meta.roles 控制访问。但我们的用户角色是动态从后端拉的,每个租户的角色权限还不一样。一开始我照着文档改,把静态路由改成异步加载,结果发现侧边栏菜单渲染不出来,或者刷新就白屏。

折腾了半天才发现,VEA 的 permission.js 里有个隐藏逻辑:它假设所有路由都是预定义的,只根据 roles 过滤。一旦你完全依赖后端返回的路由结构,它的菜单生成器就懵了。

后来我硬改了一套方案:后端直接返回扁平化的菜单数据(带 path、component、name),前端用 addRoutes 动态注册,同时自己维护一份 routeMap 用于菜单渲染。核心代码如下:

// permission.js 中修改部分
import { asyncRoutes } from '@/router/modules/asyncRoutes'

function generateRoutesFromServer(menus) {
  const accessedRoutes = menus.map(menu => {
    const route = {
      path: menu.path,
      component: Layout, // 所有页面都包一层 Layout
      children: [{
        path: 'index',
        component: loadView(menu.component),
        name: menu.name,
        meta: { title: menu.title, icon: menu.icon }
      }]
    }
    return route
  })
  return accessedRoutes
}

function loadView(view) {
  // 动态导入组件,注意路径要和实际 views 目录一致
  return () => import(@/views/${view}.vue)
}

这里注意我踩过好几次坑:loadView 里的路径必须严格匹配,否则 webpack 打包会报错;另外 addRoutes 在 Vue Router 3.5+ 已废弃,但 VEA 用的是旧版,暂时还能用。如果升级到 Vue 3 + Router 4,这套就得重写。

性能问题:首屏加载慢得离谱

本地开发没问题,一上测试环境,首屏加载 8 秒起步。打开 Network 一看,app.js 快 3MB!VEA 把所有页面组件都打包进主 chunk,Element UI 也没做按需引入。

优化分两步走:

  • 首先,Element UI 改为按需引入。装了 babel-plugin-component,然后在 main.js 里只引入用到的组件。
  • 其次,路由懒加载必须全覆盖。VEA 原始代码里有些路由还是同步 import,我全局搜了一遍,全改成 () => import('...')
// router/modules/user.js
export default [
  {
    path: 'user-list',
    name: 'UserList',
    component: () => import('@/views/user/List.vue'), // 懒加载
    meta: { title: '用户管理', icon: 'user' }
  }
]

改完后首屏 JS 降到 1.2MB,加载时间压到 2.5 秒左右。虽然还是偏大,但业务方能接受了。其实还有更狠的招——用 Webpack 分包策略拆 vendor,但当时没时间细调,留到二期再说。

一个至今没完美解决的小毛病

VEA 的 tags-view(顶部标签页)在动态路由下偶尔会重复添加。比如从 A 页面跳转到 B,再点浏览器后退,B 的标签可能还在。查了社区 issue,发现是 visitedViewscachedViews 同步逻辑有 race condition。

我临时加了个防重判断:

// store/modules/tagsView.js
addVisitedView(state, view) {
  if (state.visitedViews.some(v => v.path === view.path)) return
  state.visitedViews.push(
    Object.assign({}, view, {
      title: view.meta.title || 'no-name'
    })
  )
}

但极端情况下(比如快速连续点击多个菜单),还是会漏掉。后来干脆在 beforeEach 里加了 debounce,虽然不优雅,但线上没再复现。这个小问题影响不大,就没深究。

回头看看,值不值得用?

说实话,如果现在重新开始,我可能会选更轻量的方案,比如直接基于 Vue CLI + Element Plus 搭架子。VEA 的“全家桶”设计对新手友好,但定制成本高,很多代码你根本用不到,反而成了负担。

不过对于赶工期的项目,它确实省了大量基础工作。我们最终交付的系统稳定运行半年多,没出过大问题。权限、菜单、布局这些核心模块,VEA 都扛住了。

如果你的项目符合以下条件,可以考虑 VEA:

  • 标准后台管理系统,不需要复杂交互
  • 团队熟悉 Vue 2 + Element UI
  • 时间紧,需要快速出原型

反之,如果要做高度定制化 UI,或者打算长期迭代,建议从零搭建,避免被模板束缚。

最后贴个 API 调用示例(别问为啥用 jztheme.com)

项目里对接了一个外部数据源,接口地址是临时的,就用了个示例域名。实际代码长这样:

// api/user.js
const API_URL = 'https://jztheme.com/api'

export function fetchUserData(params) {
  return request({
    url: ${API_URL}/user/list,
    method: 'get',
    params
  })
}

注意:这只是技术演示,别真去调这个接口(笑)。

以上是我用 Vue Element Admin 做项目的完整踩坑记录。有些地方妥协了,有些地方绕过去了,但系统跑起来了,业务方满意了,这就够了。如果你也在用 VEA,遇到类似问题,或者有更好的解法,欢迎评论区交流——毕竟前端这行,谁不是一边骂框架一边改 bug 呢?

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论