webpack
如何打包资源优化你有了解吗?或者一个经常被问的面试题,首屏加载如何优化,其实无非就是从http请求
、文件资源
、图片加载
、路由懒加载
、预请求
,缓存
这些方向来优化,通常在使用脚手架中,成熟的脚手架已经给你做了最大的优化,比如压缩资源,代码的tree shaking
等。
本文是笔者根据以往经验以及阅读官方文档总结的一篇关于webpack打包
方面的长文笔记,希望在项目中有所帮助。
正文开始…
在阅读之前,本文将从以下几个点去探讨webpack的打包优化
1、webpack
如何做treeShaking
2、webpack
的gizp压缩
3、css
如何做treeShaking
,
4、入口依赖文件拆包
5、图片资源
加载优化
在官网中有提到treeShaking[1],从名字上中文解释就是摇树,就是利用esModule
的特性,删除上下文未引用的代码。因为webpack可以根据esModule
做静态分析,本身来说它是打包编译前输出,所以webpack
在编译esModule
的代码时就可以做上下文未引用的删除操作。
那么如何做treeshaking
?我们来分析下
在之前我们都是手动配置搭建webpack
项目,webpack
官方提供了cli
快速构建基本模版,无需像之前一样手动配置entry
、plugins
、loader
等
首先安装npm i webpack webpack-cli
,命令行执行`
npx webpack init
一系列初始化操作后,就生成以下代码了
// Generated using webpack-cli https://github.com/webpack/webpack-cli
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");
const isProduction = process.env.NODE_ENV == "production";
const stylesHandler = MiniCssExtractPlugin.loader;
const config = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
},
devServer: {
open: true,
host: "localhost",
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html",
}),
new MiniCssExtractPlugin(),
// Add your plugins here
// Learn more about plugins from https://webpack.js.org/configuration/plugins/
],
module: {
rules: [
{
test: /.(js|jsx)$/i,
loader: "babel-loader",
},
{
test: /.less$/i,
use: [stylesHandler, "css-loader", "postcss-loader", "less-loader"],
},
{
test: /.css$/i,
use: [stylesHandler, "css-loader", "postcss-loader"],
},
{
test: /.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
type: "asset",
},
// Add your rules for custom modules here
// Learn more about loaders from https://webpack.js.org/loaders/
],
},
};
module.exports = () => {
if (isProduction) {
config.mode = "production";
config.plugins.push(new WorkboxWebpackPlugin.GenerateSW());
} else {
config.mode = "development";
}
return config;
};
现在修改一下index.js
,并在src
中增加utils
目录
// utils/index.js
export function add(a, b) {
return a + b
}
export function square(x) {
return x * x;
}
index.js
import { add } from './utils'
console.log("Hello World!");
console.log(add(1, 2))
在index.js
中我只引入了add
,相当于square
这个函数在上下文中并未引用。
不过我还需要改下webpack.config.js
...
module.exports = () => {
if (isProduction) {
config.mode = "production";
config.plugins.push(new WorkboxWebpackPlugin.GenerateSW());
} else {
config.mode = "development";
config.devtool = 'source-map'
config.optimization = {
usedExports: true
}
}
return config;
};
注意我只增加了devtool:source-map
与optimization.usedExports = true
我们看下package.json
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"build": "webpack --mode=production --node-env=production",
"build:dev": "webpack --mode=development",
"build:prod": "webpack --mode=production --node-env=production",
"watch": "webpack --watch",
"serve": "webpack serve"
},
默认初始化已经给们预设了多个不同的打包环境,因此我只需要运行下面命令就可以选择开发环境了
npm run build:dev
/* unused harmony export square */
function add(a, b) {
return a + b;
}
function square(x) {
return x * x;
}
square
上下文未引用,虽然给了标记,但是未真正清除。
光使用usedExports:true
还不行,usedExports 依赖于 terser 去检测语句中的副作用
,因此需要借助terser
插件一起使用,官方webpack5
提供了TerserWebpackPlugin
这样一个插件
在webpack.config.js
中引入
...
const TerserPlugin = require("terser-webpack-plugin");
...
module.exports = () => {
if (isPr