Vue Router实战指南:从基础配置到高级路由控制

技术溢洋 前端 阅读 1,310
赞 44 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

上周上线一个新功能,用户反馈“点一下路由跳转要等好几秒”,我一开始还不信,直到自己在测试环境点了一下——好家伙,首页进去后点个“个人中心”,白屏 4 秒多,连 loading 都没来得及显示。这哪是 SPA,简直是 S(Slow)PA。

Vue Router实战指南:从基础配置到高级路由控制

项目用的是 Vue 3 + Vue Router 4,页面不算多,但有些路由组件里塞了大量子组件、图表、甚至内嵌的富文本编辑器。最离谱的是,有个“数据看板”页面,一进就发起 5 个 API 请求,还同步加载了 ECharts 和一堆工具函数,整个 JS bundle 直接飙到 2.8MB。首屏加载慢就算了,路由切换也卡,因为每次都是全量加载组件,完全没有懒加载。

找到瓶颈了!

先别急着改代码,得先搞清楚到底卡在哪。我打开了 Chrome DevTools 的 Performance 面板,录了一次路由跳转:

  • JS 执行时间占了 3.2s,主要是组件初始化和第三方库解析
  • Network 里看到所有路由组件的 JS 都在首页就加载完了
  • Memory 快照显示,即使离开某个页面,组件实例和事件监听器也没被清理干净

再用 Lighthouse 跑了个分,Performance 只有 38 分,主要扣分项就是“减少 JavaScript 执行时间”和“避免庞大的 DOM”。基本可以确定:问题出在路由组件的加载策略和资源体积上。

核心优化一:路由懒加载,必须上

其实 Vue Router 官方早就推荐用动态导入做懒加载,但我之前图省事,全写成同步引入了。这次咬牙全改掉。

优化前:

import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import Profile from '@/views/Profile.vue'
import Dashboard from '@/views/Dashboard.vue'

const routes = [
  { path: '/', component: Home },
  { path: '/profile', component: Profile },
  { path: '/dashboard', component: Dashboard }
]

优化后:

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', component: () => import('@/views/Home.vue') },
  { path: '/profile', component: () => import('@/views/Profile.vue') },
  { path: '/dashboard', component: () => import('@/views/Dashboard.vue') }
]

就这么简单一行改动,打包后主包体积从 2.8MB 降到 1.1MB。而且路由切换时,只有访问对应页面才加载 JS,首屏快了不少。亲测有效,但注意:这里我踩过坑——如果用 Webpack 的 magic comment 做 chunk 命名,记得加 /* webpackChunkName: "profile" */,不然生成的文件名是 0.js、1.js 这种,不利于缓存。

核心优化二:组件级懒加载 + 按需引入

光路由懒加载还不够,有些页面内部还有重型组件。比如 Dashboard 里有个地图模块,用的是高德地图 SDK,光它自己就 600KB。我把它也拆成异步组件。

<template>
  <div>
    <LazyMap v-if="showMap" />
  </div>
</template>

<script setup>
import { defineAsyncComponent } from 'vue'

const LazyMap = defineAsyncComponent(() =>
  import('@/components/HeavyMap.vue')
)

const showMap = ref(false)
// 等用户点击“显示地图”才触发加载
</script>

同时,把 ECharts 改成按需引入:

// 以前
import * as echarts from 'echarts'

// 现在
import { init } from 'echarts/core'
import { BarChart } from 'echarts/charts'
import { GridComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'

init().use([BarChart, GridComponent, CanvasRenderer])

这一套组合拳打下来,Dashboard 页面的 JS 体积从 1.2MB 降到 300KB 左右。而且用户不点地图,就不加载地图组件,体验顺滑多了。

小优化:预加载 + 缓存策略

对于高频跳转的页面(比如从首页到个人中心),可以提前预加载。Vue Router 本身不支持,但我们可以监听路由变化,在空闲时 prefetch 下一个可能访问的路由。

// 在 main.js 或 router 文件中
router.beforeEach((to, from) => {
  // 预加载高频页面
  if (from.path === '/' && to.path === '/profile') {
    // 如果还没加载过,提前触发 import
    import('@/views/Profile.vue').catch(() => {})
  }
})

另外,对已加载的路由组件,可以配合 keep-alive 缓存,避免重复渲染。但要注意:缓存太多会吃内存,所以只缓存关键页面。

<keep-alive include="Profile,Settings">
  <router-view />
</keep-alive>

这里注意我踩过好几次坑:include 的值必须是组件的 name,不是路径名!而且动态组件名要用变量,不能写死字符串(除非你确定不会变)。

性能数据对比

改完后重新跑了一遍数据(本地开发环境,关闭缓存,3G 网络模拟):

  • 首屏加载时间:从 5.2s 降到 1.1s
  • 路由跳转(首页 → 个人中心):从 4.1s 降到 780ms
  • 主包体积:2.8MB → 1.1MB
  • Lighthouse Performance 分数:38 → 82

最关键的是,用户反馈“终于不卡了”。虽然 Dashboard 第一次进还是有点慢(毕竟要加载图表),但至少不会白屏好几秒,而且后续跳转飞快。

还有一些小问题

当然,优化不是一劳永逸。现在还有两个小毛病:

  • 首次进入 Dashboard 时,图表加载仍有 1s 左右的空白,考虑加骨架屏
  • 某些低端机上,keep-alive 缓存多了会卡顿,打算加个最大缓存数限制

但这些问题不影响主流程,优先级不高,先放着。毕竟,能用就行,别过度优化。

以上是我的优化经验,有更好的方案欢迎交流

这次 Vue Router 性能优化,核心就是三点:路由懒加载、组件按需加载、合理缓存。说起来简单,但真要落地,得一个个页面抠,还得测各种机型。折腾了两天,值了。

如果你也在搞 Vue 项目,建议早点做懒加载,别等上线了被用户骂才改。这个技巧的拓展用法还有很多,比如结合 Web Worker 处理路由数据预处理,后续会继续分享这类博客。

以上是我踩坑后的总结,希望对你有帮助。有更优的实现方式?欢迎评论区交流!

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

暂无评论