这是我参与8月更文挑战的第30天,活动详情查看:8月更文挑战
前面的 webpack 掠影系列已经大致讲述了 webpack 的一些配置,这些配置可以应付大部分的工作内容,今天这一篇小札记作为 webpack 掠影的最后一篇,这一周多以来我一直在努力的整理,希望能帮到同样在搞 webpack 的你,虽然看小文章上瘾,但是不要贪多啊,最好的还是官方文档有营养,但是无论如希望这写小札记能预约你的学习路途吧;
加油,写完这一篇,下一个系列我准备写一些 HTTP 的入门内容,也当督促自己学习了,加油!加油!
一、模块懒加载
在日常的开发中,很多的模块并不需要立刻就加载,所以这些内容可以作为优化的内容在需要的再去加载,这就是资源的懒加载;webpack 同样支持这一常用的功能,webpack 的懒加载需要使用 ES6 的模块动态加载方法 import() 方法。被懒加载的模块被单独抽离成 js 文件,在需要加载的时候通过 AJAX 向服务器发起请求;import() 方法返回 Promise 实例对象;
let btn = document.getElementById('button');
btn.onclick = async function () {
let sp = await import('./test-hmr')
console.log(sp.p)
}
如上面的例子,在 btn 的 onclick 时才会触发加载 test-hmr.js 模块,我们使用了一个 async 函数来处理,如果你喜欢使用 .then 的方式来处理这,只需要 import().then() 就可以;
一般开发时,我们使用 import() 来异步加载组件,包括 vue 的异步组件或者 react 都支持这一语法,用以减小代码的体积,提升性能;
二、 dllPlugin 动态链接库
dll(Dynamic-linked-library)动态链接库,由 Microsoft 引入的概念。它提供了一种通过对 bundle 的拆分来实现提升构建速度的方法;这个插件一般配置 dllReferencePlugin 一同使用;
举一个例子,我们项目开发时,我们使用 react 技术栈,一般情况下,我们使用某一个版本的 react 确定后短时间内不需要更改,但是像这种比较大的库,我们每次打包的时候都会被重新打包,这就导致构建速度十分缓慢,所以我们可以吧 react 和 react-dom 单独抽离出来制成动态链接库,这样一来我们开发业务代码的时候再次打包就不会再打包 react 和 react-dom ,打包速度将会得到明显的提升。
2.1 首先配置一个打包文件,把像 react、react-dom 这种第三方的库单独打包出来;
webpack.react.config.js该配置文件需要在output中增加额外的配置项目:
library: 和下面的 libraryTarget 配合使用;把文件打成一个库,这个字段的值将会成为一个变量,这个变量代表的是上面entry的导出内容;可以修改,同样支持 [name] 的方式libraryTarget: 和上面的library配合生效;表示用什么方式把entry导出的内容赋值给变量,默认值是var
所以 library 和 libraryTarget 设置的结论就是: var __dll_xx = react+react-dom 导出的内容;
let path = require('path');
let webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
react: ['react', 'react-dom']
},
output: {
filename: '__dll_xx.js', // 产生的 js 文件名
path: path.resolve(__dirname, 'manifest'),
library: '__dll_xx',
libraryTarget: 'var'
// 所以 library 和 libraryTarget 设置的结论就是: var __dll_xx = react+react-dom 导出的内容;
},
plugins: [
new webpack.DllPlugin({
name: '__dll_xx',
path: path.resolve(__dirname, 'manifest', 'manifest.json')
})
]
}
2.2 执行 webpack 命令打包输出 manifest 文件
- 在 package.json 中加入命令
"scripts": {
"dev": "webpack-dev-server",
"build": "webpack",
+ "build-react": "webpack --config ./webpack.react.config.js"
},
- 执行
npm run build-react
执行命令后,我们会发现在在 dist 目录下生成了两个文件,__dll_xx.js 和 manifest.json。这个 js 就是一个由我们刚刚配置了 DllPlugin 打包输出的库,而这个 manifest.json 就是一个映射表文件;
- 生成的文件如图:
js 文件的名字就是我们配置的 __dll_xx.js
__dll_xx.js代码中的第一个变量如图
var __dll_xx = func.... 就是:libraryTarget 和 library 指定的结果
2.3 在模板中引入 DLL 库
我们在模板 other.html 中引入,DLL 库的 js 文件;
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root-other-page"></div>
<p>this is another page </p>
+ <script src="/__dll_xx.js"></script>
</body>
</html>
2.4 DllReferencePlugin
上面我们已经成功创建了 DLL 库,接下来我们需要在我们的业务代码中使用我们的 DLL 库;需要在 webpack 的 plugins 中增加一条插件;
module.exports = {
....
plugins: [
+ new webpack.DllReferencePlugin({
+ manifest: path.resolve(__dirname, 'dist', 'manifest.json')
+ })
]
...
}
三、 happypack 多线程打包
当项目很大的时候,打包速度很可能会很慢,webpack 支持多线程打包。这个功能依赖于 happypack
3.1 安装 happypack
yarn add happypack
3.2 导入 happypack
- 修改配置文件
- 修改
loaderuse: 'happypack/loader?id=js', 使用happypack时,需要将loader改成happypack/loader?id=xx这个xx是一个标识符,下面的plugin还需要用 - 修改
plugins
- 修改
const path = require('path');
const webpack= require('webpack');
const Happypack = require('happypack');
module.exports = {
mode: 'production',
devtool: 'eval-source-map',
entry: {
home: './src/index.js',
other: './src/other.js',
x: './src/x.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js' // 配合多页面应用 [name] 表示文件名, [hash:5] 生成文件名后跟随 5 位 hash 戳记
},
devServer: {
},
module: {
rules: [
+ {
+ test: /.(js|jsx)$/,
+ use: 'happypack/loader?id=js', // 使用 happypack 时,需要将 loader 改成 'happypack/loader?id=xx' 这个 xx 是一个标识符,下面的 plugin 还需要用
+ exclude: /node_modules/
}
]
},
plugins: [
+ // 使用 happypack 多线程打包 js
+ new Happypack({
+ id: 'js', // 这个 'js' 就是上面配置 loader 时,写在 happypack/loader?id=js 中的 js
+ use: [
+ {
+ loader: 'babel-loader',
+ options: {
+ presets: ['@babel/preset-env', '@babel/preset-react'],
+ plugins: [
+ ['@babel/plugin-proposal-decorators', { legacy: true }], // 使用装饰器
+ ['@babel/plugin-proposal-class-properties', { loose: true }], // 使用 class-properties
+ ['@babel/plugin-transform-runtime'] // 使用 async/await/generator
+ ]
+ }
+ }
+ ]
+ })
],
// 配置优化配置项
optimization: {
}
}
注意:使用 happypack 时,如果包体积很小,则改善不会十分明显,还有可能会比不使用的情况变的更差,这是因为使用 happypack 时会增加线程通信的开销,这些增量再打包时间不是很长的情况下就会变的不可忽略,所以不建议滥用!
四、小结
本次掠影主要介绍了2中常见的优化打包速度的解决方案:Dll 和 happypack,这两种方式本质上不能减小总代码体积的。
- 第一种
Dll的解决方案是让由一部分代码不再参与打包,减轻打包的工作量。 happypack则是人海战术了,活儿多不怕,加人干就行了,但是注意值得一提的是,注意happypack本身是会带来一定的时间增量的,如果包体积不大,不建议滥用!
五、给八月
八月最后一天,最遗憾的就是第一天错过了,没有机会写满一个整月。拿什么奖品不重要了,重要的是我一次性把文章补了起来;
过了今天要歇歇了,感谢观众老爷的阅读!