Vue 项目中使用 webpack 将多个组件合并打包并实现按需加载

8

使用 Vue 开发项目时,如果要使用其单文件组件特性,必然要使用 webpack 或者 Browserify 进行打包,对于大型应用,为了提升加载速度,可以使用 webpack 的 code splitting 功能进行分割打包,生成较小的模块并按需加载,这在 Vue 文档及 vue-router 文档中均有介绍:Async ComponentsLazy Loading

webpack 的 code splitting 可以使用 webpack 的 require.ensure 特殊语法或者使用 AMD 风格的 callback-require 语法,以 AMD 风格的 callback-require 语法为例——

全局注册 Async Component:

let myAsyncComponent = resolve => {
  require(['./my-async-component'], resolve)
}
Vue.component('async-webpack-example', myAsyncComponent)

局部注册 Async Component,单文件组件中 script 块内容:

let myAsyncComponent = resolve => {
  require(['./my-async-component'], resolve)
}

// Vue 扩展实例选项,其他选项略
export default {
  components: {
    'async-webpack-example': myAsyncComponent
  }
}

在使用 vue-router 时,为实现不同路由下的组件异步加载,在路由映射中可以使用同样的方式来设置路由项的 component 属性。

这里的 myAsyncComponent 被定义为一个工厂函数,在需要时才会以 Vue 或者 vue-router 定义的用于解析组件选项的 resolve 回调函数(是的,在 Vue 和 vue-router 中有两个不同的解析组件选项的函数)为参数执行 callback-require 函数(resolve 回调函数的参数是组件选项),这样,在执行打包脚本时,my-async-component.vue 文件会被单独打包成一个文件,并且仅当该组件被使用时才会加载。

当要求异步加载的组件较多时,将会生成更多的单个文件,对于前端性能而言,虽然每个文件更小了,但可能意味着更多的网络连接建立和关闭的开销,因此在前端优化的实践中,通常需要在文件数量和单个文件大小之间取得平衡。

本文介绍如何将多个组件合并打包成一个单独的文件,一方面可以减少代码块的数量,另一方面,如果合并打包的这些组件在不同地方多次重复使用,由于 Vue 的缓存机制,可以加快后续组件的加载速度,并且如果这些通用组件长时间不会变化(如 UI 相关的组件),打包生成的文件也长期不会变化,可以充分利用浏览器的缓存功能,实现前端加载速度的优化。

先上效果图,在使用 vue-router 的 SPA 应用中,将除根路由之外的路由项对应的 ComponentA、ComponentB、ComponentC 等三个组件合并打包成一个文件。初次加载页面时,从开发者工具的 Network 面板上可以看到,此时未加载包含 ComponentA、ComponentB、ComponentC 这三个组件的 0.a5a1bae6addad442ac82.js 文件,当点击 Page A 链接时,加载了该文件,然后再点击 Page B、Page C 链接时,没有重新加载该文件。

result

我们首先通过 vue-cli 命令行工具使用 webpack 项目模板创建一个包含 vue-router 的项目,在其 src/components 目录下创建一个 CommonComponents 目录,在该目录中创建 ComponentA、ComponentB、ComponentC 这三个组件。

CommonComponents

同时在 CommonComponents 目录下创建 index.js,其内容如下:

exports.ComponentA = require('./ComponentA')
exports.ComponentB = require('./ComponentB')
exports.ComponentC = require('./ComponentC')

这样,我们只需要使用 webpack 的 require.ensure 特殊语法或者使用 AMD 风格的 callback-require 语法异步加载 CommonComponents 目录下的 index.js,在使用 webpack 进行打包时,就可以实现将 ComponentA、ComponentB、ComponentC 这三个组件合并打包。以 AMD 风格的 callback-require 语法为例示范如下,这里的 callback 回调函数的形式没有任何特殊要求。

require(['components/CommonComponents'], function (CommonComponents) {
  // do whatever you want with CommonComponents
})

components/CommonComponents 模块加载成功时,这里的回调函数中的 CommonComponents 参数将会是一个包含 ComponentA、ComponentB、ComponentC 这三个组件选项的对象。

在定义异步组件时,我们使用的是一个工厂函数 resolve => {require(['./my-async-component'], resolve)},如果需要在路由配置文件中添加 component 属性为 ComponentA 组件的路由项,应该定义什么样的工厂函数呢?记住这里的 resolve 是一个用于解析组件选项的回调函数,其参数是所获取的组件选项,而上一段代码中的 CommonComponents 恰好是包含若干个组件选项的对象,因此我们可以将 CommonComponents 的子属性作为参数用于 resolve 调用,我们编写一个函数 getCommonComponent,用于根据组件名称返回获取相应的组件选项的工厂函数。

let getCommonComponent = componentName => resolve => require(['components/CommonComponents'], components => resolve(components[componentName]))

在组件模板或者路由映射等使用其中某一个组件的地方,可以使用类似于 getCommonComponent('ComponentA') 这样的函数调用进行组件设置,在路由映射中的使用示例如下:

routes: [
  {
    path: '/',
    name: 'Hello',
    component: Hello
  },
  {
    path: '/a',
    name: 'A',
    component: getCommonComponent('ComponentA')
  },
  {
    path: '/b',
    name: 'B',
    component: getCommonComponent('ComponentB')
  },
  {
    path: '/c',
    name: 'C',
    component: getCommonComponent('ComponentC')
  }
]

最终打包生成的文件列表如下图所示,其中的 0.a5a1bae6addad442ac82.js 包含了 ComponentA、ComponentB、ComponentC 这三个组件。

built


进阶学习

SF 讲堂《学习 Vue 你需要知道的 webpack 知识》


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

11 条评论
吖木 · 2017年09月18日

你好,请问能提供一下demo的代码吗?

回复

1

基本上照着打一遍就OK了啊

KingMario 作者 · 2017年09月18日
张浩 · 2017年09月22日

CommonComponents 下的index在什么时候引入的
getCommonComponent 方法中的require(['components/CommonComponents'] 中的components/CommonComponents是什么意思
新手求问

回复

0

就是引入components/CommonComponents/index.js的写法,只是省略了index.js

这次可以有 · 5月3日
KingMario 作者 · 2017年09月22日

components/CommonComponents = components/CommonComponents/index.js
这是 Node.js 中的规范,webpack 使用此规范

回复

jieyi · 1月26日

你好,为什么我打包生成的文件0.XXXXXXXXXXXXXXX.js 文件只有1kb,代码只有一行。这样对吗?

回复

jieyi · 3月1日

@KingMario 帮忙看看

回复

0

具体代码及配置是什么样的?你可以用格式化工具把生成的文件格式化一下,看看具体内容和你的原始文件是否相关。

KingMario 作者 · 3月1日
0
webpackJsonp([0],{841:function(e,a,s){a.one=s(275),a.two=s(274),a.three=s(277),a.four=s(276)}});
//# sourceMappingURL=0.b1703e36a33175c7d0a3.js.map

这是生成后的那个0.xxxxxxxxxxxxxxxxx.js,并没有相关的业务代码,只有一个引用。
我是将one,two,...,...,四个组件按照本文操作。
是不是需要修改webpack.xxx.conf.js文件的配置。
不好意思,语言组织有点乱...

jieyi · 3月2日
0

项目是使用vue-cli生成的,webpack配置没有怎么改动。

jieyi · 3月2日
载入中...