对于大型web app来说,如果把所有的文件都打包到一个文件中是非常低效的,特别是当一些代码块只在某些特定的条件下被调用。webpack可以让你的代码库分割成不同的块(chucks),仅仅在需要的时候再加载。其他的一些打包工具叫它们图层(layers), 卷(rollups) 或者 片段(fragments),这些特性被叫做代码分离(code splitting)。
这是一个可选的属性。你可以在你的代码库中定义分割点。webpack能帮你管理好依赖,输出文件和运行时。
澄清一个共同的误解:代码分离(code splitting)不仅仅是抽出公共代码把它们放进一个共享的块(chuck)中。更重要的特性是代码分离(code splitting)可以将代码分割成按需加载的块。这可以使项目初始化的时候只需要加载很少的代码,当应用请求的时候再按需加载代码。
有三种常用的代码分离方法:
- 入口起点: 使用entry选项手动分离代码
- 防止重复: 使用 CommonsChuckPlugin 来去重和分离代码
- 动态导入: 在模块内部通过内联函数来调用代码
入口
这是迄今为止最简单,最直观的拆分代码的方式。但是它是比较偏向手动并且有一些陷阱需要我们去注意的。看一看我们是怎么样从主buddle中分割其他模块的。
project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- index.js
+ |- another-module.js
|- /node_modules
another-module.js
import _ from 'lodash';
console.log(
_.join(['Another', 'module', 'loaded!'], ' ')
);
webpack.config.js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
这将会产生下面的build结果:
Hash: 309402710a14167f42a8
Version: webpack 2.6.1
Time: 570ms
Asset Size Chunks Chunk Names
index.bundle.js 544 kB 0 [emitted] [big] index
another.bundle.js 544 kB 1 [emitted] [big] another
[0] ./~/lodash/lodash.js 540 kB {0} {1} [built]
[1] (webpack)/buildin/global.js 509 bytes {0} {1} [built]
[2] (webpack)/buildin/module.js 517 bytes {0} {1} [built]
[3] ./src/another-module.js 87 bytes {1} [built]
[4] ./src/index.js 216 bytes {0} [built]
还记得我们提到的那些陷阱嘛?
- 如果入口文件中有任何重复的模块,它们都会被包含在这些入口文件中。
- 它是不灵活的并且不能用内在的应用逻辑来动态分割代码。
这两点中的第一点绝对是我们例子的中的问题。像lodash
也在./src/index.js中被导入,所以它会在两个bundle中重复。让我们来使用CommonsChunkPlugin来移除这个重复的部分。
阻止重复
CommonsChunkPlugin插件允许我们抽出共同的依赖放在一个存在的入口chunk或者一个存在的new chunk里。让我们使用这个插件去除上一个例子中重复的lodash依赖。
const path = require('path');
+ const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
+ })
+ }),
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'common' // Specify the common bundle's name.
+ })
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
在这里使用commonsChunkPlugin,我们将会看到重读的依赖会从我们的index.bundle.js文件中移除。插件将会注意到我们已经将loadash分割成一个单独的块。并且从我们的主bundle中删除了这部分。让我们运行npm run bulid
来看看它是否起作用了。
Hash: 70a59f8d46ff12575481
Version: webpack 2.6.1
Time: 510ms
Asset Size Chunks Chunk Names
index.bundle.js 665 bytes 0 [emitted] index
another.bundle.js 537 bytes 1 [emitted] another
common.bundle.js 547 kB 2 [emitted] [big] common
[0] ./~/lodash/lodash.js 540 kB {2} [built]
[1] (webpack)/buildin/global.js 509 bytes {2} [built]
[2] (webpack)/buildin/module.js 517 bytes {2} [built]
[3] ./src/another-module.js 87 bytes {1} [built]
[4] ./src/index.js 216 bytes {0} [built]
这里有一些社区提供的分割代码的插件和加载器:
- ExtractTextPlugin: 用来从主程序中分割出css
- bundle-loader: 用来分离代码并且懒加载结果bundles
- Promise-loader: 和bundle-loader作用相同,但是它是使用promise
CommonsChunkPlugin使用explicit vendor chunks从核心应用代码中来分离vendor模块
动态导入
当涉及到动态分割的时候,有两种相似的技术被支持。第一个并且也是更完满的方法是使用遵循ECMAScript规范的import语法来进行动态导入。更老的一种是,webpack特殊的方法require.ensure。让我们来试一试使用第一种方法。
import调用使用promise,如果你想在不支持promise的老旧浏览器上使用,请引入一个promise polyfill。
在我们开始之前,让我们移除这个多余的entry和commonsChunkPlugin 从我们的配置文件中。因为它们在接下来的教学中已经不再需要了。
webpack.config.js
const path = require('path');
- const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
+ index: './src/index.js'
- index: './src/index.js',
- another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
- }),
+ })
- new webpack.optimize.CommonsChunkPlugin({
- name: 'common' // Specify the common bundle's name.
- })
],
output: {
filename: '[name].bundle.js',
+ chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。