路由懒加载

当打包构建应用时,Javascript 包会变得非常大,影响页面加载速度。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

结合 Vue 的异步组件路由懒加载 - 图1和 Webpack 的代码分割功能路由懒加载 - 图2,轻松实现路由组件的懒加载。如:

  1. const Foo = () => import('./Foo.vue')

当你觉得你的页面热更新速度慢的时候,才需要往下看 ↓

区分开发与生产环境 [该方案已淘汰]

当你的项目页面越来越多之后,在开发环境之中使用 lazy-loading 会变得不太合适,每次更改代码触发热更新都会变得非常的慢。所以建议只在生产环境之中使用路由懒加载功能。

开发环境:

  1. module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

这里注意一下该写法只支持 vue-loader at least v13.0.0+理由 vue-element-admin/issues/231路由懒加载 - 图3

生产环境:

  1. module.exports = file => () => import('@/views/' + file + '.vue')

淘汰原因

当然这样写会有一些副作用。由于

Every module that could potentially be requested on an import() call is included. For example, import(./locale/${language}.json) will cause every .json file in the ./locale directory to be bundled into the new chunk. At run time, when the variable language has been computed, any file like english.json or german.json will be available for consumption.

@/views/下的 .vue 文件都会被打包。不管你是否被依赖。所以这样就产生了一个副作用,就是会多打包一些可能永远都用不到 js 代码。当然这只会增加 dist 文件的大小,但不会对线上代码产生任何的副作用。相关 issue路由懒加载 - 图4

TIP

用户自己可以根据业务情况来衡量一下是否采用本方案,如果你的项目页面不超过几十个,本地开发热更新速度你还能接受的话,可以直接所有环境下都是用懒加载避免此副作用。

新方案

使用babelplugins babel-plugin-dynamic-import-node路由懒加载 - 图5。它只做一件事就是将所有的import()转化为require(),这样就可以用这个插件将所有异步组件都用同步的方式引入,并结合 BABEL_ENV路由懒加载 - 图6 这个babel环境变量,让它只作用于开发环境下,在开发环境中将所有import()转化为require(),这种方案解决了之前重复打包的问题,同时对代码的侵入性也很小,你平时写路由的时候只需要按照官方文档路由懒加载 - 图7路由懒加载的方式就可以了,其它的都交给babel来处理,当你不想用这个方案的时候,也只要将它从babelplugins中移除就可以了。

具体代码:

首先在package.json中增加BABEL_ENV

  1. "dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"

接着在.babelrc只能加入babel-plugin-dynamic-import-node这个plugins,并让它只有在development模式中才生效。

  1. {
  2. "env": {
  3. "development": {
  4. "plugins": ["dynamic-import-node"]
  5. }
  6. }
  7. }

之后就大功告成了,路由只要像平时一样写就可以了。

  1. { path: '/login', component: () => import('@/views/login/index')}

相关代码改动路由懒加载 - 图8

vue-cli@3 [该方案已淘汰]

vue-element-admin@4 在新版本中已修改为基于 vue-cli来进行构建。所以在新版本中你只要在.env.development环境变量配置文件中设置VUE_CLI_BABEL_TRANSPILE_MODULES:true就可以了,具体代码路由懒加载 - 图9

它的实现逻辑和原理与之前还是一样的,还是基于babel-plugin-dynamic-import-node来实现的。之所以在vue-cli中只需要设置一个变量就可以了,是借用了vue-cli它的默认配置,它帮你代码都写好了。通过阅读源码路由懒加载 - 图10可知,vue-cli会通过VUE_CLI_BABEL_TRANSPILE_MODULES这个环境变量来区分是否使用babel-plugin-dynamic-import-node,所以我们只要开启它就可以。虽然它的初衷是为了单元测试的,但正好满足了我们的需求。

废弃原因

vue-cli@3时代,使用VUE_CLI_BABEL_TRANSPILE_MODULES是 ok 的,但其实也是脆弱的,就比如在vue-cli@4时,vue-cli 引入babel-plugin-dynamic-import-node的逻辑就发生了变化,需要VUE_CLI_BABEL_TRANSPILE_MODULESVUE_CLI_BABEL_TARGET_NODE同时为 true 时才会生效,所以只要 vue-cli 的判断逻辑发生了变化,我们都需要做相对应的改动,或非常被动和耦合。所以我们在vue-cli@4版本中,不再通过VUE_CLI_BABEL_TRANSPILE_MODULES:true来设置,而是通过手动引入'babel-plugin-dynamic-import-node'的方式,具体见下一部分。

vue-cli@4

  1. .env.development文件中不在需要配置VUE_CLI_BABEL_TRANSPILE_MODULES = true,删除即可。

  2. 在命令行执行 npm install babel-plugin-dynamic-import-node -S -D

  3. babel.config.js 中添加插件

  1. module.exports = {
  2. presets: ['@vue/cli-plugin-babel/preset'],
  3. env: {
  4. development: {
  5. plugins: ['dynamic-import-node']
  6. }
  7. }
  8. }

改进

webpack5 即将发布,大幅提高了打包和编译速度,之后可能完全不需要搞这么复杂了,再多的页面热更新,都能很快,完全就不需要前面提到的解决方案了。