代码分割是webpack的一个重要特性,可以将代码分割成不同的部分(bundle),以便进行后续的按需加载和并行下载,对提升项目性能起着重要的作用
方法一:webpack入口文件配制成多个文件
const path = require('path');
module.exports = {
//entry也可以是数组,例如entry:['./src/index.js', './src/another-module.js']
//为数组时不会生成多个bundle
//此时entry是对象
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
output: {
//[name]是entry的键值,[hash]是打包时候的hash值
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
看上去分割成多个bundle,但是会出现以下问题
- 如果入口bundle包含多个相同模块(例如index.js和another-module.js都引入了 lodash)则会打包两遍
- 这种方法不够灵活,完全依照配置打包,并且不能将核心应用程序逻辑进行动态拆分代码
⚠️:解决1的问题可以使用CommonsChunkPlugin(已在 webpack v4
中移除,类似插件可以采用SplitChunksPlugin,官网地址)插件来解决重复打包相同文件问题
const path = require('path');
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // bundle 名称
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
此时可以看到打包文件中多出common bundle,这个bundle就是抽离出来的公共模块
方法二:动态导入
Webpack 的动态分割主要方式是使用符合 ECMAScript 提案的 import() 语法。例如import(’./a.js’).then(_module => {})。
import()会返回一个Promise对象,意味着这个模块和它的子模块都会被分割成一个单独的 bundle。
在Vue项目中,动态分割是实现按需加载的前提,按需加载需要实现以下要求:
- 将每个路由对应的组件都打包成一个单独的bundle(根据import()已经实现)
- 当路由被访问的时候才加载该路由对应的 bundle(用到 vue 里面的异步组件特性)
Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
//resolve返回一个fullfilled的Promise,而 import() 也是返回 Promise
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
使用 import() 后,工厂函数的写法:
Vue.component('async-webpack-example',
// 该 `import` 函数返回一个 `Promise` 对象。
() => import('./my-async-component')
)
最后在 vue-router 的路由配置中,我们只需要这么写:
const router = new VueRouter({
routes: [
{ path: '/login', component: () => import('@/views/login'), },
{ path: '/home', component: () => import('@/views/home'), }
]
})
方法三:预获取/预加载模块
Webpack官方是希望我们使用异步的方式来进行模块的加载的。而且异步加载也能提升首页加载速度,但是又会导致异步加载的那部分代码逻辑迟迟不能执行,可能导致用户的交互长时间没有响应。
这个时候就需要prefetch或preload了。
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出"resource hint(资源提示)",来告知浏览器:
- prefetch(预获取):将来某些导航下可能需要的资源(指示浏览器在空闲时间加载)
- preload(预加载):当前导航下可能需要资源(会在父 bundle 加载时,以并行方式开始加载)
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
//实际上这样做,Webpack替我们在head内添加了这样一行:
<link rel="prefetch" as="script" href="0.js">