打包策略
- 选择合适的打包粒度,生成的单文件大小不要超过500KB
- 充分利用浏览器的并发请求,同时保证并发数不超过6
- 尽可能让浏览器命中304,频繁改动的业务代码不要与公共代码打包
- 避免加载太多用不到的代码,层级较深的页面进行异步加载
基于以上原则,我选择的打包策略如下:
- 第三方库如vue、jquery、bootstrap打包为一个文件
- 公共组件如弹窗、菜单等打包为一个文件
- 工具类、项目通用基类打包为一个文件
- 各个功能模块打包出自己的入口文件
- 各功能模块作用一个SPA,子页面进行异步加载
代码分离
- 入口起点:使用
entry
配置手动地分离代码。 - 防止重复:使用 Entry dependencies 或者
SplitChunksPlugin
去重和分离 chunk。 - 动态导入:通过模块的内联函数调用来分离代码。
动态导入
-
使用import () 语法
-
使用webpack遗留功能: require.ensure
Warning
import()
调用会在内部用到 promises。如果在旧版本浏览器中(例如,IE 11)使用import()
,记得使用一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shimPromise
。
预获取、预加载模块
预获取
//...
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
这会生成 <link rel="prefetch" href="login-modal-chunk.js">
并追加到页面头部,指示着浏览器在闲置时间预取 login-modal-chunk.js
文件。
Tip
只要父 chunk 完成加载,webpack 就会添加 prefetch hint(预取提示)。
与 prefetch 指令相比,preload 指令有许多不同之处:
- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
- 浏览器支持程度不同。
预加载
import(/* webpackPreload: true */ 'ChartingLibrary');
打包(bundle) 分析(bundle analysis)
一旦开始分离代码,一件很有帮助的事情是,分析输出结果来检查模块在何处结束。 官方分析工具 是一个不错的开始。还有一些其他社区支持的可选项:
- webpack-chart: webpack stats 可交互饼图。
- webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
- webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。
- webpack bundle optimize helper:这个工具会分析你的 bundle,并提供可操作的改进措施,以减少 bundle 的大小。
- bundle-stats:生成一个 bundle 报告(bundle 大小、资源、模块),并比较不同构建之间的结果。
依赖管理
require(’./template’ + name + ‘.ejs’)
你还可以通过 require.context()
函数来创建自己的 context。
可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。
require.context(
directory,
(useSubdirectories = true),
(regExp = /^\.\/.*$/),
(mode = 'sync')
)
require.context('../', true, /\.stories\.js$/);
// (创建出)一个 context,其中所有文件都来自父文件夹及其所有子级文件夹,request 以 `.stories.js` 结尾。
如果想引入一个文件夹下面的所有文件,或者引入能匹配一个正则表达式的所有文件,这个功能就会很有帮助,例如:
const cache = {};
function importAll(r) {
r.keys().forEach((key) => (cache[key] = r(key)));
}
importAll(require.context('../components/', true, /\.js$/));
// 在构建时(build-time),所有被 require 的模块都会被填充到 cache 对象中。
Tree Shaking
我们学到为了利用 tree shaking 的优势, 你必须…