用webpack来打包你的单页应用的一个可能的缺点是你可能会得到一个非常大的打包文件,有时甚至会有几兆大小。
Asset | Size |
---|---|
bundle.main.js | 1.36MB |
随之带来的问题是用户在屏幕上看到任何东西之前,必须要下载整个文件并运行它。如果用户使用的是网络连接不好的设备,这个过程可能需要很长时间。
Code splitting 就是把打包文件分割成为更小的文件,允许用户在他们需要的时候下载需要的代码块的一个方法。
例如,看看这个简单的界面,我们可以识别出这个App中不需要初始载入的部分。
如果我们延迟加载这些代码段直到初始渲染结束会怎样?它可以让用户更快的看到界面并进行交互。
在这篇文章,我将向你展示如何使用Vue.js 和 Webpack将单页应用拆分成可以动态加载的更理想的大小。
异步组件
Code splitting 的关键就是异步组件(async components),这个组件的定义就是异步加载(包括它的模板,数据,方法等等)。
首先让我们用component API 即 Vue.component(name, definition) 声明一个组件。不同于将定义对象作为第二个参数,异步组件有一个函数,这个函数有两个显著的特点:
- 它执行 Promise,拥有一个 resolve 参数
- 它是一个工厂函数,也就是说它返回一个对象(在本例中是组件定义)
Vue.component('async-component', (resolve) => {
resolve({
template: '<div>Async Component</div>',
props: [ 'myprop' ]
});
});
异步组件是 Code splitting 的第一步,因为我们现在才有了一个抽象的机制。
动态模块加载
我们还需要 webpack 的帮助,假设我们将组件定义抽象为ES6模块文件:
AsyncComponent.js
export default {
template: '<div>Async Component</div>',
props: [ 'myprop' ]
}
我们怎么让Vue.js加载它呢?你可能会想试试这样的方法:
import AsyncComponent from './AsyncComponent.js'`;
Vue.component('async-component', AsyncComponent);
但是这是静态的并且在编译时解析,如果我们想要获得Code splitting 的好处,我们需要的是在运行的应用中动态地加载。
import()
目前,用JavaScript来动态的加载模块是不可能的。但是,ECMAScript有一个动态模块加载函数叫做 import() 。
Webpack已经有了一个import()的实现,并将其作为一个代码分割点,在创建包时,将所请求的模块放入一个单独的文件中(一个单独的chunk,实际上,现在把它看作单独的一个文件)。
import() 将文件名作为一个参数并返回一个Promise。下面是我们如何加载上面的模块:
import(/* webpackChunkName: "async-component" */ './AsyncComponent.js')
.then((AsyncComponent) => {
console.log(AsyncComponent.default.template);
// Output: <div>Async Component</div>
});
注:如果你使用Babel,你需要添加 syntax-dynamic-import 插件以便Babel可以正确的解析。
现在,但你构建项目的时候,你会注意到这些模块出现在它自己的文件中了:
Asset | Chuck Name |
---|---|
bundle.main.js | main |
bundle.0.js | async-component |
你可以给动态导入的模块加一个名字,让它更容易识别;只需在文件名前面添加一个注释,就像我在上面的例子中所做的那样。
动态组件加载
因为 import() 返回一个Promise,我们可以将它和 Vue 的异步组件功能结合使用。Webpack将单独打包异步组件,并通过AJAX将其动态加载到应用程序中。
main.js
import Vue from 'vue';
Vue.component('async-component', (resolve) => {
import('./AsyncComponent.js')
.then((AsyncComponent) => {
resolve(AsyncComponent.default);
});
});
new Vue({
el: '#app'
});
index.html
<div id="app">
<p>This part is included in the page load</p>
<async-component></async-component>
</div>
<script src="bundle.main.js"></script>
页面初始加载时:
<div id="app">
<p>This part is included in the page load</p>
</div>
当main.js运行的时候,它将启动异步组件模块的请求(这是自动实现的,因为 webpack 的 import() 包含了AJAX加载模块的代码)。
如果Ajax成功返回模块,Promise 完成并且渲染组件,页面将会被渲染成这样:
<div id="app">
<p>This part is included in the page load</p>
<div>Async Component</div>
</div>
下图可以更好地帮助你理解:
single file component
Vue 实现 Code Splitting 的特殊方式就是使用 single file component。下面是使用single file component的代码重构:
AsyncComponent.vue
<template>
<div>Async Component</div>
</template>
<script>
export default {
props: [ 'myprop' ]
}
</script>
导入的语法也更加简洁
new Vue({
el: '#app',
components: {
AsyncComponent: () => import('./AsyncComponent.vue')
}
});
Code Splitting 架构
这是无关技术的部分,问题就是我们怎么为一个app构建一个code splitting ?
最直接的方法就是按照界面来,比如,你的app上有两个界面,一个home,一个about。这两个界面是由两个组件(home.vue about.vue)构成,那么这两个组件就可以是拆分的点。
另外,你也可以对任何有条件的组件(选项卡、弹窗、下拉菜单等等)或者是footer进行拆分。
原文地址:Code Splitting with Vue.js And Webpack
译者:Damer Wang
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。