记录优化细节
1.分包的优势
不分包
- 会导致bundle文件很大很混乱,不方便管理。
- 而文件很大的话,下载很慢。可能导致长时间的白屏
优化方案
- 分包处理(prefetch)
- ssr (1.加快首屏渲染 2.增加seo优化)
- 代码分离(分离更小更多的bundle,加快渲染)
代码分离的三种方式
1. 配置entry,多个入口
这样做就会出现问题,二个入口文件都使用axios,那个打包出来的文件每个里面都有axios源代码.可以配置entry的shared进行解决
这种方式很少使用,像vue/react都已将三方包打包的一个vendors文件中进行共享
2. 动态导入
就像vue-router点击跳转按钮才会加载文件并渲染页面
需要更改分包后的名字
或者使用魔法注释
3. 使用SplitChunksPlugin进行去重和分离代码
webpack默认安装和集成SplitChunksPlugin.使用axios,其打包的文件在默认的主包bundle.js文件中.
为了让三方包进行分离,打包到单独的js文件中.解决办法如下:
配置之后就会进行分包,其中包含异步包和三方包
自己设置分包的大小
自己对需要拆分的内容进行分包,vue/react脚手架就是这样拆分的(异步包,主包,三方包)
对生成包的名字进行优化(id作为打包文件的名字)
2.prefetch和preload(代码中进行配置)
prefetch: 预获取,主包加载完之后,再加载预获取的包 preload: 预加载,和主包一起下载下来
点击按钮之后,从预获取的缓存中进行读取
3.CDN
相互连接的网络系统,服务器距离用户越近,传输更快更可靠.
修改publicPath属性,设置cdn的地址
第三方包使用cdn地址.
4.shimming(了解)
shimming翻译为垫片. 可以通过ProvidePlugin实现shimming的效果.
场景: 默认没有导入某个库,直接使用时就会报错. shimming可以预置全局变量
const { ProvidePlugin } = require("webpack");
不推荐这样去写,还是要按照模块化的方式去编写代码
5.提取css文件
使用css要配置css-loader和style-loader
提取css到单独的文件需要使用mini-css-extract-plugin插件.因为要提取css,使用此插件的loader
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
将一类文件放在文件夹中(js文件也是类似)
5.1对打包的文件进行命名
hash: 通过MD4的散列函数处理后,生成一个128位的hash值(32个十六进制)
1.hash: 改了源代码,hash都会改变.比如: 两个入口,hash都会改变 2.contenthash: 只有修改的文件hash才会改变(推荐) 3.chunkhash: 与contenthash一样,但chunkhash会根据不同入口解析来生成
6.Terser(压缩和丑化代码的工具集)
打包完成之后,bundle.js有压缩的效果,是因为底层使用TerserPlugin插件.
而terser是独立的工具,可以进行全局或者局部的安装
npm install terser -g/-D
命令行使用(了解)
上面的方式只能优化到一行,还有-c -m等等的配置项.
webpack中进行配置使用
在webpack中有minimizer属性,在production模式下,默认使用terserplugin来处理代码.
如果压缩之后没有达到预期的效果,可以自己配置minimizer
注意: production默认会打开minimize,要压缩的话建议在development和production都打开.
上面是对js代码进行压缩,接下来对css进行压缩
注意: minimize和minimizer必须在optimization中.
7.配置分离
配置不同环境不同的变量
修改common.config.js的导出
将common的内容复制到dev和prod文件夹中,依次进行修改
注意: 因为要将common.config.js在不同的环境中与dev或者prod合并,可以使用插件进行合并.
// common.config.js
const path = require("path"); // 导入path模块
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { ProvidePlugin } = require("webpack");
// 进行合并
const { merge } = require("webpack-merge");
const devConfig = require("./dev.webpack");
const prodConfig = require("./prod.webpack");
const commonConfig = (isDev) => {
return {
// entry: "./src/index.js",
output: {
clean: true,
path: path.resolve(__dirname, "../build"),
filename: "[name]-bundle.js",
chunkFilename: "[name]_chunk.js",
},
// 文件名后缀
resolve: {
extensions: [".js", ".json", ".wasm", ".jsx", ".ts"],
},
// loader
module: {
rules: [
{
test: /\.css$/i,
use: [
// 通过传参来区分环境使用不同的loader
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
// "style-loader",
// MiniCssExtractPlugin.loader, // 使用这个loader对css进行提取
"css-loader",
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
title: "Fhup",
}),
new ProvidePlugin({
axios: ["axios", "default"],
}),
],
};
};
module.exports = function (env) {
const isDev = env.dev;
if (isDev) {
// 将commonConfig变为函数,就可以进行传参进行判断
return merge(commonConfig(isDev), devConfig);
} else {
return merge(commonConfig(isDev), prodConfig);
}
};
// dev.webpack.js
module.exports = {
mode: "development",
devServer: {
static: ["public", "src/js"],
open: true,
port: 7777,
proxy: {
"/api": {
target: "http://123.207.32.32:8000",
pathRewrite: { "^/api": "" },
secure: false,
changeOrigin: true,
},
},
// historyApiFallBack: true,
},
};
// prod.webpack.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserPlugin = require("terser-webpack-plugin"); // 安装webpack,默认进行安装
const CssMinimizerWebPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
mode: "production",
devtool: "source-map",
// 优化配置
optimization: {
// 设置生成的chunkid的算法
// development: named
// production: deterministic(确定性)
chunkIds: "deterministic",
splitChunks: {
chunks: "all", // async / all
// 当一个包大于指定大小时,继续进行拆包
// maxSize: 20000, // 20kb
// minSize: 10,
// 自己对需要拆分的内容进行分包
cacheGroups: {
vendors: {
// node_modules文件夹(加入 \/ (mac和windows二种文件路径解析格式 \ 和 / ,而第一个 \ 是转义)
// 导入三方包查找路径: ./node_moudules/
test: /[\\/]node_modules[\\/]/,
filename: "[id]_f_vendors.js",
},
utils: {
// utils包比较小,大于20kb才会分包 所以要设置minSize
test: /utils/, // utils文件夹
filename: "[id]_utils.js",
},
},
},
minimize: true, // development下将其设置为true,terser才能进行压缩
minimizer: [
// minimizer中包含js压缩插件和css压缩插件
new TerserPlugin({
extractComments: false, // 启用/禁用剥离注释功能
terserOptions: {},
}),
// 对css进行压缩
new CssMinimizerWebPlugin({}),
],
},
plugins: [
// 完成css的提取
new MiniCssExtractPlugin({
filename: "css/[contenthash:6]_style.css",
chunkFilename: "css/[contenthash:6]_chunk_style.css",
}),
],
};
再贴上未分离的webpack.config.js
// 没有分离之前的配置项
const path = require("path"); // 导入path模块
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { ProvidePlugin } = require("webpack");
const TerserPlugin = require("terser-webpack-plugin"); // 安装webpack,默认进行安装
const CssMinimizerWebPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
mode: "development",
// devtool: "source-map",
// entry: "./src/index.js",
// 多入口配置
// entry: {
// index: {
// import: "./src/index.js",
// dependOn: "shared1",
// },
// main: {
// import: "./src/main.js",
// dependOn: "shared1",
// },
// shared1: ["axios"],
// // shared2: ["dayjs", "redux"],
// },
output: {
clean: true,
path: path.resolve(__dirname, "./build"),
// filename: "js/bundle.js", // bundle会生成到js文件夹下
filename: "[name]-bundle.js", // 二个入口对应二个bundle.js 可以使用name进行占位
// 针对单独分包的文件进行命名
chunkFilename: "[name]_chunk.js",
// 打包后文件的路径前缀(设置cdn地址)
// publicPath: "http://fhup/cdn",
},
devServer: {
open: true,
},
// 优化配置
optimization: {
// 设置生成的chunkid的算法
// development: named
// production: deterministic(确定性)
chunkIds: "deterministic",
splitChunks: {
chunks: "all", // async / all
// 当一个包大于指定大小时,继续进行拆包
// maxSize: 20000, // 20kb
// minSize: 10,
// 自己对需要拆分的内容进行分包
cacheGroups: {
vendors: {
// node_modules文件夹(加入 \/ (mac和windows二种文件路径解析格式 \ 和 / ,而第一个 \ 是转义)
// 导入三方包查找路径: ./node_moudules/
test: /[\\/]node_modules[\\/]/,
filename: "[id]_f_vendors.js",
},
utils: {
// utils包比较小,大于20kb才会分包 所以要设置minSize
test: /utils/, // utils文件夹
filename: "[id]_utils.js",
},
},
},
minimize: true, // development下将其设置为true,terser才能进行压缩
minimizer: [
// minimizer中包含js压缩插件和css压缩插件
new TerserPlugin({
extractComments: false, // 启用/禁用剥离注释功能
terserOptions: {},
}),
// 对css进行压缩
new CssMinimizerWebPlugin({}),
],
},
// loader
module: {
rules: [
{
test: /\.css$/i,
use: [
// "style-loader",
MiniCssExtractPlugin.loader, // 使用这个loader对css进行提取
"css-loader",
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
title: "Fhup",
}),
new ProvidePlugin({
// axios: "axios", // 从axios中提供全局变量axios
// axios导出比较特殊
axios: ["axios", "default"],
}),
// 完成css的提取
new MiniCssExtractPlugin({
filename: "css/[contenthash:6]_style.css",
chunkFilename: "css/[contenthash:6]_chunk_style.css",
}),
],
};
8.TreeShaking (针对dev,自己配置)
usedExports 标记要删除的函数
打包时没有进行使用的函数默认不会打包,而dev环境不会被删除
针对dev: 标记要删除的函数,使用terser进行删除. 而prod默认开启进行删除
配置的terser压缩代码
sideEffects 告知webpack那些文件有副作用
dev环境: 如果只导入文件,那么文件里的导出的函数和执行的逻辑代码连同文件一起被打包.而我只是导入文件,没有使用任何的函数,配置sideEffects可以删除整个模块.
这样在treeshaking时都会被优化,但配置sideEffects就要注意代码的编写.
不要出现含有副作用的代码,尽量编写纯模块.
告诉webpack,math模块有副作用不要进行treeshaking
注意: 这样写的话,导入的css也会被treeshaking掉.所以要在sideEffects数组中添加 "*.css"
css的treeshaking
css压缩时不会将className压缩为一个字母,那么浏览器就会识别不到.css的treeshaking只能将没有使用的class进行移除,并将代码压缩到一行.
PurgeCss插件: 删除未使用的css ==> pnpm add purgecss-webpack-plugin -D
注意: 安装glob 7.* 版本,不然paths拿到的是空数组
打包时删除未使用的css
Scope Hoisting (作用域提升)将函数合并到一个模块中运行
打包后的代码外层会有函数作用域. 因为导入其他文件的函数进行使用,二个在不同的模块中,使用函数的那个模块还得导入要使用的函数,不如将函数直接放到使用函数的那个模块中.也就是作用域提升.
production默认进行作用域提升,dev环境配置Scope Hoisting
9.http压缩
对代码进行http压缩,浏览器拿到http压缩过的文件自动进行解压.
压缩格式: deflate(deflate算法压缩) gzip(常用) br(开源压缩算法 http内容编码而设计)
webpack使用 pnpm install compression-webpack-plugin -D
针对js和css进行压缩
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
plugins: [
// http压缩js和css
new CompressionPlugin({
algorithm: "gzip", // 压缩算法
test: /\.(css|js)$/, // 配置的文件
// threshold: 10240, // 从多大开始压缩
minRatio: 0.7, // 压缩比例
}),
]
}
可以直接部署.gz
针对html进行压缩(注意: minify也是对应一个插件)
const HtmlWebpackPlugin = require("html-webpack-plugin");
10.打包速度分析
pnpm install speed-measure-webpack-plugin -D
特别注意: SpeedMeasurePlugin可能会与MiniCssExtractPlugin不兼容,建议注释 MiniCssExtractPlugin(接下来的二张截图)
11.打包后文件分析
pnpm add webpack-bundle-analyzer -D
plugins: [
// 对打包后文件进行分析
new BundleAnalyzerPlugin(),
],
执行完npm run build,默认在浏览器打开这个页面(查看包的大小 来源 压缩后大小...)