为什么需要优化
不管是为了面试还是开发,作为一个前端佬,webpack优化都是非常重要的事情。不仅可以提高用户的体验,还可以减轻服务器的压力
一、提升开发体验优化
sourceMap源代码映射
开发时我们的代码都是经过webpack编译后的。一旦项目体积大起来。如果此时代码运行出错,它只会提示编译后的代码,而编译后的代码是很难查看错误的。编译后代码示例如下:
那么怎么查看源代码的错误呢。正所谓有需求就有市场。这时候sourceMap就应运而生了。
sourceMap是一个用来生成源代码到构建后代码的映射。它会生成一个map文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过map文件,从构建后的代码出错位置找到源代码的出错位置。从而让开发者更快的进行错误溯源。
webpack官方文档提供了很多devtool,不同的devtool构建和重新构建的速度也不一样
实际开发中我们只需关注两种:
- 开发模式:cheap-module-source-map
- 优点:构建速度快(相较于
source-map),只包含行映射 - 缺点:没有列映射
- 优点:构建速度快(相较于
module.exports = {
...
mode: 'development',
devtool: 'cheap-module-source-map'
}
- 生产模式:source-Map
- 优点:包含行映射和列映射
- 缺点:构建速度慢
module.exports = {
...
mode: 'production',
devtool: 'source-map'
}
二、提高打包构建速度
HMR热更新
开发时我们修改了其中一个模块代码,webpack会默认将所有模块进行打包编译,速度很慢。
如果想做到按需打包,可以使用HMR。一般只用于开发模式
module.exports = {
...
mode: 'development',
devServer: {
host: 'localhost',
port: 3000,
open: true, //是否自动打开浏览器
hot: true, //是否启用热更新
}
}
但是当JS改变是不会热更新的,如果需要热更新,可以在入口文件添加这段语句
if(module.hot) { //如果浏览器支持热更新的话
module.hot.accept('xxx') //xxx是需要做热更新的JS文件的路径
}
当然如果文件多的话,这样写会很麻烦,替换方案就是使用vue-loader、react-hot-loader
特别注意: 从 webpack-dev-server v4.0.0 开始,热模块替换是默认开启的。所以现在是默认开启热更新的,即使你不配置!
oneOf
当进行规则匹配时,会从我们写的rules从头找到尾,这样明显拖慢了打包速度。oneOf就是当匹配时,只使用第一个匹配到的规则
module.exports = {
//...
module: {
rules: [
{
test: /.css$/,
oneOf: [
{
resourceQuery: /inline/, // foo.css?inline
use: 'url-loader',
},
{
resourceQuery: /external/, // foo.css?external
use: 'file-loader',
},
],
},
],
},
};
include / exclude
include: 需要处理的文件exclude: 不需要处理的文件
实际上node_modules中的文件是不需要进行打包的,这些文件已经被作者编译过了!我们不需要做无用功了!
module.exports = {
...
//使用include来指定编译文件夹
include: path.resolve(__dirname, '../src'),
//使用exclude排除指定文件夹
exclude: /node_modules/
}
注意:这两种方式只能用其中一种,否则会报错!
Cache
每次打包时,重新打包资源会比较慢,我们可以缓存资源,提高二次构建的速度。一般是对JS文件进行语法检查和babel编译耗费的时间比较多。所以主要对这里eslint和babel产生的文件进行缓存
对babel进行缓存
{
test: /\.(js)$/,
include: path.resolve(__dirname,'../src'),
loader: 'babel-loader',
options: {
cacheDirectory: true, //开启babel缓存
cacheCompression: false //关闭缓存文件压缩。development模式下默认为 false,production模式默认为gzip
}
}
对eslint进行缓存
plugins: [
new ESLintPlugin({
context: path.reslove(__dirname,'../src'),
cache: true,
cacheLocation: path.reslove(__dirname,'../node_modules/cache/eslintCache') //缓存文件存放位置
})
]
多进程打包
使用多进程打包,可以有效的提高打包的速度
我们能启动的最大的核数取决于我们的电脑,运行以下代码可以查看电脑的内核数量
const os = require('os')
console.log(os.cpus().length) //我的电脑是12
下载
npm install --save-dev thread-loader
使用
use: [
{
loader: 'thread-loader',
works: 12 //进程数量
},
...
]
三、减小代码体积
Tree Shaking
通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。这个术语和概念实际上是由 ES2015 模块打包工具 rollup普及起来的。
目前,Webpack5已经默认开启了此功能无需进行配置
减小babel生成文件的体积
babel为编译的每个文件都插入了辅助代码,使得代码体积过大!
babel对一些公共方法使用了非常小的辅助代码,比如_extend,默认情况下会被添加到每一个需要它的文件中
你可以将这些辅助代码作为一个独立模块,来避免重复引入
下载
npm i @babel/plugin-transform-runtime -D
使用
{
test: /\.(js)$/,
include: path.resolve(__dirname,'../src'),
loader: 'babel-loader',
options: {
cacheDirectory: true, //开启babel缓存
cacheCompression: false //关闭缓存文件压缩。development模式下默认为 false,production模式默认为gzip
plugins: ['@babel/plugin-transform-runtime']
}
}
压缩图片
开发中如果项目中引用了很多图片,将来请求速度会比较慢。 所以我们可以对图片进行压缩,减小图片体积。如果引入的是在线链接,就不需要压缩了
下载
npm i image-minimizer-webpack-plugin imagemin -D
无损压缩
npm i imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
有损压缩
npm i imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
使用
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); //引入插件
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),