今天偶然发现,打开自己的博客网站时特别慢,
可以看到其中一个js下载了11s才下载完,有2.6MB(这还是nginx做了gzip压缩之后的大小),nginx压缩前的大小如下:
可以看到这个js竟然高达10MB,才想起来,我忘记做webpack的打包压缩了。OK,优化走起:
一、下载压缩相关的库
# js 压缩
npm install uglifyjs-webpack-plugin --save-dev
# css提取
npm install mini-css-extract-plugin --save-dev
# css压缩
npm install css-minimizer-webpack-plugin --save-dev
# 读取环境变量
npm install cross-env --save-dev
二、配置 webpack.config.js[2023-02-16更新]
const path = require("path")
const webpack = require("webpack")
const HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html入口文件
const {CleanWebpackPlugin} = require('clean-webpack-plugin') // 编译文件时,清理 build/dist 目录,再生成新的
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const isProduction = process.env.NODE_ENV === "production"
const isDevelopment = process.env.NODE_ENV === "development"
console.log("print env: ", isProduction)
module.exports = {
entry: {
app: path.resolve(__dirname, "src/index.tsx"),
},
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
// 配置信息
{
targets: {
"chrome": "58",
"ie": "11",
},
// 指定 corejs 的版本
"corejs": "3",
// 使用 corejs 的方式 "usage" 表示按需加载
"useBuiltIns": "usage"
}
],
[
"@babel/preset-typescript",
],
[
"@babel/preset-react",
{}
]
]
}
},
{
loader: "ts-loader" // 1. 先加载ts
}
],
},
{
test: /\.(css|scss)$/,
use: [
{
loader: process.env.NODE_ENV === "production"
? MiniCssExtractPlugin.loader // 提取css到文件中,放到head中
: "style-loader", // 4. 加载样式文件到head
},
{
loader: "css-loader", // 3. 加载css
options: {
importLoaders: 1,
sourceMap: true
}
},
{
loader: "postcss-loader", // 2. 加载postcss(项目里配置的是 autoprefixer 和 px2rem) 转换 css里的rem和厂商前缀
options: {
postcssOptions: {
config: path.resolve(__dirname, 'postcss.config.js')
},
sourceMap: true
}
},
{
loader: "sass-loader", // 1. 加载 scss 转换成css
options: {
sourceMap: true
}
}
]
},
// {
// test: /\.(png|jpg|gif)$/,
// use: [
// {
// loader: 'file-loader',
// options: {
// name: '[sha512:hash:base64:7].[ext]'
// }
// }
// ]
// } // by junfenghe 2021-12-06
{
test: /\.(png|jpg|gif|ico)$/,
type: 'asset/resource',
generator: {
filename: 'static/media/[hash][ext][query]'
}
},
{
test: /\.svg/,
type: 'asset/inline'
},
// 20230216 打包html中的图片
{
test: /\.(htm|html)$/i,
loader: "html-withimg-loader"
}
// {
// test: /\.(txt|pdf|excel)$/,
// type: 'asset/source'
// }
]
},
resolve: {
extensions: ["*", ".js", ".jsx", ".ts", ".tsx"], // 尝试按顺序解析这些后缀名。能够用户再引入模块时不带扩展,例如:import File from '../path/to/file'
},
output: {
path: path.resolve(__dirname, "build"), // 文件输出的路径
// filename: "bundle.js", //对于单一入口(entry point)起点,filename 会是一个静态名称;
filename: "static/js/[name].[fullhash].bundle.js", // 决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。// 当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin) 创建多个 bundle,应该赋予每个 bundle 一个唯一的名称
assetModuleFilename: "images/[hash][ext][query]" // by junfenghe 2021-12-06 // by junfenghe 20221228
},
//20230216
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
`...`,
new CssMinimizerPlugin({
parallel: true,//使用多进程并发执行,
minimizerOptions:{
preset:[
"default",
{
discardComments: {removeAll:true},//移除所有注释
},
]
}
}),
]
},
//20230216
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public/index.html')
}),
new CleanWebpackPlugin({
path: path.join(__dirname, 'build')
}),
// // 20230216 代码压缩
// new UglifyJsPlugin({
// parallel: true,// 使用多进程并行以提高构建速度
// sourceMap: true,// 使用源映射将错误信息位置映射到模块(这将会减慢编译速度)。
// // extractComments:true,//启用禁用提取注释
// cache: true,//启用缓存
// uglifyOptions: {
// comments: false,//如果你构建时不想出现注释,可以按照以下配置将 uglifyOptions.output.comments 设置为 false:
// },
// }),
// // 提取css
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new CssMinimizerPlugin()
],
devServer: {
static: {
directory: path.join(__dirname, "public")
},
historyApiFallback: true, //When using the HTML5 History API, the index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true:
port: 3003,
compress: true
},
devtool: isProduction?false:'source-map',//生产环境关闭sourcemap
}
if (isProduction) {
pluginsProduction = [
// 20230216 代码压缩
new UglifyJsPlugin({
parallel: true,// 使用多进程并行以提高构建速度
sourceMap: true,// 使用源映射将错误信息位置映射到模块(这将会减慢编译速度)。
// extractComments:true,//启用禁用提取注释
cache: true,//启用缓存
uglifyOptions: {
comments: false,//如果你构建时不想出现注释,可以按照以下配置将 uglifyOptions.output.comments 设置为 false:
},
}),
]
module.exports.plugins.push(
...pluginsProduction
)
}
三、基于 React.lazy() 的优化
原来的代码:
import DimensionReading from "../../pages/dimension/dimension-reading/dimension-reading";
import DimensionWriting from "../../pages/dimension/dimension-writing/dimension-writing";
import DimensionPhoto from "../../pages/dimension/dimension-photo/dimension-photo";
import DimensionPlan from "../../pages/dimension/dimension-plan/dimension-plan";
import DimensionAdd from "../../pages/dimension/dimension-add/dimension-add";
import DimensionHome from "../../pages/dimension/dimension-home/dimension-home";
import SelfIntro from "../../pages/self-intro/self-intro";
优化后的代码:
const DimensionReading = lazy(() => import("../../pages/dimension/dimension-reading/dimension-reading"))
const DimensionWriting = lazy(() => import("../../pages/dimension/dimension-writing/dimension-writing"))
const DimensionPhoto = lazy(() => import("../../pages/dimension/dimension-photo/dimension-photo"));
const DimensionPlan = lazy(() => import("../../pages/dimension/dimension-plan/dimension-plan"));
const DimensionAdd = lazy(() => import("../../pages/dimension/dimension-add/dimension-add"));
const DimensionHome = lazy(() => import("../../pages/dimension/dimension-home/dimension-home"));
const SelfIntro = lazy(() => import("../../pages/self-intro/self-intro"));
-
React Lazy 是一个 React 的高级特性,可以使得在使用 Webpack 或者其他打包工具进行构建时,能够按需加载组件,从而减少首次加载页面的时间和网络流量。React Lazy 配合 React Suspense 一起使用可以实现更好的体验。
- 这是webpack打包之后的效果,可以看到:每个使用lazy()高级特性的组件,都能独立打包,网页加载时也就达到了按需加载的目的了。
四、查看优化效果
优化完,直接打包之后的大小如下:
-
可以看到最大的这个js从10MB变成了4MB,接着看部署之后的实际效果:
- 可以看到,经过nginx的gzip压缩之后,从原先的2.6MB,变成了1.3MB。
五、总结
写到这儿,打包压缩的性能优化已经告一段落了,这块其实以前也做过一点,现在也算回顾了一下,接下来可以把一些静态文件(包括js、css、img等等)上传至cdn,来进一步加速,各位友友有其他性能优化的建议,也可以一起交流一波,互相学习学习!
网友评论