Webpack 5-高级优化-减少代码体积

2023年05月06日 11:04 · 阅读(188) ·

依赖环境

名称 版本
操作系统 Windows 10 X64
Node.js Nodejs 16+
Webpack 8.5.0
Webpack 5-基础 -

来源

名称 版本
视频地址 尚硅谷Webpack5入门到原理(面试开发一条龙)
视频+资料下载 百度云资料
课件下载 Webpack5-课件.zip
Webpack 文档 https://webpack.docschina.org/concepts/

减少代码体积

Tree Shaking

为什么

开发时我们定义了一些工具函数库,或者引用第三方工具函数库或组件库。

如果没有特殊处理的话我们打包时会引入整个库,但是实际上可能我们可能只用上极小部分的功能。

这样将整个库都打包进来,体积就太大了。

是什么

Tree Shaking 是一个术语,通常用于描述移除 JavaScript 中的没有使用上的代码。

注意:它依赖 ES Module

怎么用

Webpack 已经默认开启了这个功能,无需其他配置。

Babel

为什么

Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!

Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。

你可以将这些辅助代码作为一个独立模块,来避免重复引入。

是什么

@babel/plugin-transform-runtime: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。

怎么用

  1. 下载包
  1. npm i @babel/plugin-transform-runtime -D
  1. 配置
  1. const os = require("os");
  2. const path = require("path");
  3. const ESLintWebpackPlugin = require("eslint-webpack-plugin");
  4. const HtmlWebpackPlugin = require("html-webpack-plugin");
  5. const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  6. const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
  7. const TerserPlugin = require("terser-webpack-plugin");
  8. // cpu核数
  9. const threads = os.cpus().length;
  10. // 获取处理样式的Loaders
  11. const getStyleLoaders = (preProcessor) => {
  12. return [
  13. MiniCssExtractPlugin.loader,
  14. "css-loader",
  15. {
  16. loader: "postcss-loader",
  17. options: {
  18. postcssOptions: {
  19. plugins: [
  20. "postcss-preset-env", // 能解决大多数样式兼容性问题
  21. ],
  22. },
  23. },
  24. },
  25. preProcessor,
  26. ].filter(Boolean);
  27. };
  28. module.exports = {
  29. entry: "./src/main.js",
  30. output: {
  31. path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
  32. filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
  33. clean: true,
  34. },
  35. module: {
  36. rules: [
  37. {
  38. oneOf: [
  39. {
  40. // 用来匹配 .css 结尾的文件
  41. test: /\.css$/,
  42. // use 数组里面 Loader 执行顺序是从右到左
  43. use: getStyleLoaders(),
  44. },
  45. {
  46. test: /\.less$/,
  47. use: getStyleLoaders("less-loader"),
  48. },
  49. {
  50. test: /\.s[ac]ss$/,
  51. use: getStyleLoaders("sass-loader"),
  52. },
  53. {
  54. test: /\.styl$/,
  55. use: getStyleLoaders("stylus-loader"),
  56. },
  57. {
  58. test: /\.(png|jpe?g|gif|webp)$/,
  59. type: "asset",
  60. parser: {
  61. dataUrlCondition: {
  62. maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
  63. },
  64. },
  65. generator: {
  66. // 将图片文件输出到 static/imgs 目录中
  67. // 将图片文件命名 [hash:8][ext][query]
  68. // [hash:8]: hash值取8位
  69. // [ext]: 使用之前的文件扩展名
  70. // [query]: 添加之前的query参数
  71. filename: "static/imgs/[hash:8][ext][query]",
  72. },
  73. },
  74. {
  75. test: /\.(ttf|woff2?)$/,
  76. type: "asset/resource",
  77. generator: {
  78. filename: "static/media/[hash:8][ext][query]",
  79. },
  80. },
  81. {
  82. test: /\.js$/,
  83. // exclude: /node_modules/, // 排除node_modules代码不编译
  84. include: path.resolve(__dirname, "../src"), // 也可以用包含
  85. use: [
  86. {
  87. loader: "thread-loader", // 开启多进程
  88. options: {
  89. workers: threads, // 数量
  90. },
  91. },
  92. {
  93. loader: "babel-loader",
  94. options: {
  95. cacheDirectory: true, // 开启babel编译缓存
  96. cacheCompression: false, // 缓存文件不要压缩
  97. plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
  98. },
  99. },
  100. ],
  101. },
  102. ],
  103. },
  104. ],
  105. },
  106. plugins: [
  107. new ESLintWebpackPlugin({
  108. // 指定检查文件的根目录
  109. context: path.resolve(__dirname, "../src"),
  110. exclude: "node_modules", // 默认值
  111. cache: true, // 开启缓存
  112. // 缓存目录
  113. cacheLocation: path.resolve(
  114. __dirname,
  115. "../node_modules/.cache/.eslintcache"
  116. ),
  117. threads, // 开启多进程
  118. }),
  119. new HtmlWebpackPlugin({
  120. // 以 public/index.html 为模板创建文件
  121. // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
  122. template: path.resolve(__dirname, "../public/index.html"),
  123. }),
  124. // 提取css成单独文件
  125. new MiniCssExtractPlugin({
  126. // 定义输出文件名和目录
  127. filename: "static/css/main.css",
  128. }),
  129. // css压缩
  130. // new CssMinimizerPlugin(),
  131. ],
  132. optimization: {
  133. minimize: true,
  134. minimizer: [
  135. // css压缩也可以写到optimization.minimizer里面,效果一样的
  136. new CssMinimizerPlugin(),
  137. // 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了
  138. new TerserPlugin({
  139. parallel: threads // 开启多进程
  140. })
  141. ],
  142. },
  143. // devServer: {
  144. // host: "localhost", // 启动服务器域名
  145. // port: "3000", // 启动服务器端口号
  146. // open: true, // 是否自动打开浏览器
  147. // },
  148. mode: "production",
  149. devtool: "source-map",
  150. };

Image Minimizer

为什么

开发如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢。

我们可以对图片进行压缩,减少图片体积。

注意:如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩。

是什么

image-minimizer-webpack-plugin: 用来压缩图片的插件

怎么用

  1. 下载包
  1. npm i image-minimizer-webpack-plugin imagemin -D

还有剩下包需要下载,有两种模式:

  • 无损压缩
  1. npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
  • 有损压缩
  1. npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D

有损/无损压缩的区别

  1. 配置

我们以无损压缩配置为例:

  1. const os = require("os");
  2. const path = require("path");
  3. const ESLintWebpackPlugin = require("eslint-webpack-plugin");
  4. const HtmlWebpackPlugin = require("html-webpack-plugin");
  5. const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  6. const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
  7. const TerserPlugin = require("terser-webpack-plugin");
  8. const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
  9. // cpu核数
  10. const threads = os.cpus().length;
  11. // 获取处理样式的Loaders
  12. const getStyleLoaders = (preProcessor) => {
  13. return [
  14. MiniCssExtractPlugin.loader,
  15. "css-loader",
  16. {
  17. loader: "postcss-loader",
  18. options: {
  19. postcssOptions: {
  20. plugins: [
  21. "postcss-preset-env", // 能解决大多数样式兼容性问题
  22. ],
  23. },
  24. },
  25. },
  26. preProcessor,
  27. ].filter(Boolean);
  28. };
  29. module.exports = {
  30. entry: "./src/main.js",
  31. output: {
  32. path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
  33. filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
  34. clean: true,
  35. },
  36. module: {
  37. rules: [
  38. {
  39. oneOf: [
  40. {
  41. // 用来匹配 .css 结尾的文件
  42. test: /\.css$/,
  43. // use 数组里面 Loader 执行顺序是从右到左
  44. use: getStyleLoaders(),
  45. },
  46. {
  47. test: /\.less$/,
  48. use: getStyleLoaders("less-loader"),
  49. },
  50. {
  51. test: /\.s[ac]ss$/,
  52. use: getStyleLoaders("sass-loader"),
  53. },
  54. {
  55. test: /\.styl$/,
  56. use: getStyleLoaders("stylus-loader"),
  57. },
  58. {
  59. test: /\.(png|jpe?g|gif|svg)$/,
  60. type: "asset",
  61. parser: {
  62. dataUrlCondition: {
  63. maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
  64. },
  65. },
  66. generator: {
  67. // 将图片文件输出到 static/imgs 目录中
  68. // 将图片文件命名 [hash:8][ext][query]
  69. // [hash:8]: hash值取8位
  70. // [ext]: 使用之前的文件扩展名
  71. // [query]: 添加之前的query参数
  72. filename: "static/imgs/[hash:8][ext][query]",
  73. },
  74. },
  75. {
  76. test: /\.(ttf|woff2?)$/,
  77. type: "asset/resource",
  78. generator: {
  79. filename: "static/media/[hash:8][ext][query]",
  80. },
  81. },
  82. {
  83. test: /\.js$/,
  84. // exclude: /node_modules/, // 排除node_modules代码不编译
  85. include: path.resolve(__dirname, "../src"), // 也可以用包含
  86. use: [
  87. {
  88. loader: "thread-loader", // 开启多进程
  89. options: {
  90. workers: threads, // 数量
  91. },
  92. },
  93. {
  94. loader: "babel-loader",
  95. options: {
  96. cacheDirectory: true, // 开启babel编译缓存
  97. cacheCompression: false, // 缓存文件不要压缩
  98. plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
  99. },
  100. },
  101. ],
  102. },
  103. ],
  104. },
  105. ],
  106. },
  107. plugins: [
  108. new ESLintWebpackPlugin({
  109. // 指定检查文件的根目录
  110. context: path.resolve(__dirname, "../src"),
  111. exclude: "node_modules", // 默认值
  112. cache: true, // 开启缓存
  113. // 缓存目录
  114. cacheLocation: path.resolve(
  115. __dirname,
  116. "../node_modules/.cache/.eslintcache"
  117. ),
  118. threads, // 开启多进程
  119. }),
  120. new HtmlWebpackPlugin({
  121. // 以 public/index.html 为模板创建文件
  122. // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
  123. template: path.resolve(__dirname, "../public/index.html"),
  124. }),
  125. // 提取css成单独文件
  126. new MiniCssExtractPlugin({
  127. // 定义输出文件名和目录
  128. filename: "static/css/main.css",
  129. }),
  130. // css压缩
  131. // new CssMinimizerPlugin(),
  132. ],
  133. optimization: {
  134. minimizer: [
  135. // css压缩也可以写到optimization.minimizer里面,效果一样的
  136. new CssMinimizerPlugin(),
  137. // 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了
  138. new TerserPlugin({
  139. parallel: threads, // 开启多进程
  140. }),
  141. // 压缩图片
  142. new ImageMinimizerPlugin({
  143. minimizer: {
  144. implementation: ImageMinimizerPlugin.imageminGenerate,
  145. options: {
  146. plugins: [
  147. ["gifsicle", { interlaced: true }],
  148. ["jpegtran", { progressive: true }],
  149. ["optipng", { optimizationLevel: 5 }],
  150. [
  151. "svgo",
  152. {
  153. plugins: [
  154. "preset-default",
  155. "prefixIds",
  156. {
  157. name: "sortAttrs",
  158. params: {
  159. xmlnsOrder: "alphabetical",
  160. },
  161. },
  162. ],
  163. },
  164. ],
  165. ],
  166. },
  167. },
  168. }),
  169. ],
  170. },
  171. // devServer: {
  172. // host: "localhost", // 启动服务器域名
  173. // port: "3000", // 启动服务器端口号
  174. // open: true, // 是否自动打开浏览器
  175. // },
  176. mode: "production",
  177. devtool: "source-map",
  178. };
  1. 打包时会出现报错:
  1. Error: Error with 'src\images\1.jpeg': '"C:\Users\86176\Desktop\webpack\webpack_code\node_modules\jpegtran-bin\vendor\jpegtran.exe"'
  2. Error with 'src\images\3.gif': spawn C:\Users\86176\Desktop\webpack\webpack_code\node_modules\optipng-bin\vendor\optipng.exe ENOENT

我们需要安装两个文件到 node_modules 中才能解决, 文件可以从课件中找到:

  • jpegtran.exe

需要复制到 node_modules\jpegtran-bin\vendor 下面

jpegtran 官网地址

  • optipng.exe

需要复制到 node_modules\optipng-bin\vendor 下面

OptiPNG 官网地址