人人都会AI编程

12.6 路由懒加载与代码分割

更新时间:2026-07-09

随着应用功能增长,打包后的 JavaScript 文件会越来越大。如果用户访问首页时就要下载整个应用的所有代码,首屏加载时间会被严重拖慢——尤其对于移动网络或弱网用户,几 MB 的 JS 可能意味着好几秒的白屏。

路由懒加载(Route Lazy Loading)是解决这个问题的核心手段:只在用户真正访问某个路由时,才去加载该路由对应的组件代码。配合构建工具的代码分割(Code Splitting),每个路由的组件会被拆成独立的 chunk 文件,按需请求。

基础实现:动态导入

Vue Router 直接支持动态导入语法 import(),它返回一个 Promise,Vue Router 会在路由被访问时自动解析这个 Promise 并渲染组件。

同步加载(不推荐)

// router/index.js
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
]

这种写法会把 Home.vueAbout.vue 都打进主包(app.js),用户访问首页时,About 页面的代码也被下载了,即便他可能永远不会点进去。

懒加载(推荐)

const routes = [
  { path: '/', component: () => import('@/views/Home.vue') },
  { path: '/about', component: () => import('@/views/About.vue') }
]

Vite 或 Webpack 在构建时遇到 import() 会将该文件提取为一个独立的 chunk(如 About-abc123.js)。用户访问 / 时只加载主页 chunk,只有当用户点击“关于”链接时,浏览器才会发起请求加载 About-abc123.js 并渲染。

带加载状态与错误处理

网络请求可能较慢或失败,单纯的 () => import(...) 无法提供过渡反馈。可以结合 Vue 3 的 defineAsyncComponent 进行更精细的控制:

import { defineAsyncComponent } from 'vue'

const About = defineAsyncComponent({
  loader: () => import('@/views/About.vue'),
  loadingComponent: LoadingSpinner,  // 加载中显示的组件
  errorComponent: ErrorDisplay,      // 加载失败显示的组件
  delay: 200,                        // 200ms 后才显示 loading,避免闪烁
  timeout: 10000                     // 超时视为失败
})

const routes = [
  { path: '/about', component: About }
]

对于大多数场景,直接使用 () => import(...) 已经足够,只有在需要定制加载/错误 UI 时才用到 defineAsyncComponent

分组打包(Magic Comments)

默认每个 import() 会生成一个独立的 chunk。有时你希望把几个相关路由的组件打包在同一个 chunk 中(比如同一业务模块下的子页面),可以用魔法注释指定 webpackChunkName 或 vite 对应的输出文件名。

const routes = [
  {
    path: '/settings',
    component: () => import(/* webpackChunkName: "settings" */ '@/views/Settings.vue')
  },
  {
    path: '/settings/profile',
    component: () => import(/* webpackChunkName: "settings" */ '@/views/Profile.vue')
  }
]

构建后,这两个组件会被合并到一个名为 settings.[hash].js 的 chunk 里。这样做的好处是:当用户进入设置模块时,相关的子页面代码可以一次取回,减少后续导航的网络请求次数。注意:不要把所有路由都塞进一个 chunk,那就失去了懒加载的意义。

路由懒加载的实践建议

  • 默认全部懒加载:除了应用最核心的布局组件或全局通用的组件外,几乎所有路由页面都应该写成动态导入。这是性价比最高的优化之一。
  • 首屏考虑预加载:对于用户从首页大概率会立即跳转的关键页面(比如登录后直接进入的仪表盘),可以在首页加载完成后利用 <link rel="prefetch">router.isReady() 后手动预加载,让后续跳转变瞬时。
  • 不要为了拆而拆:过度的分割会导致过多的 HTTP 请求(在 HTTP/1.1 下尤其明显)。好在现代构建工具和 HTTP/2 已经极大缓解了这个问题,但仍建议按业务模块合理分组。
  • 监控 chunk 大小:用 rollup-plugin-visualizerwebpack-bundle-analyzer 分析构建产物,确保单个 chunk 体积不会过大(如超过 500KB,视项目可接受标准而定)。

懒加载本质上是用按需获取来换取首屏速度,是移动端优化的必选项,也是 Vue 项目性能基线的一部分。Vue Router 对动态导入的原生支持让这个优化实施成本几乎为零。