入门webpack的最佳实践(基于webpack4.X 5.X)-- 打包结果优化

2022-09-04 20:39:59 浏览数 (1)

导语

来到这家公司之后,一直在使用webpack,也写了不少笔记,但是都比较零散,现在决定整理一下webpack相关的知识点,由浅入深,方便自己后续查漏补缺,后续会一直更新。

前言

在上一篇文章中,简单介绍了提升构建速度的几种途径,而构建的产物,我们也想尽量让它体积小一点,在本文中,将从几个方面,介绍webpack如何对构建结果进行优化。

目录
  • 打印体积分析
  • 压缩css
  • 压缩js
  • 清除无用css
  • tree shaking
  • scope hosting
  • 删除无用代码

打印体积分析

借助插件 webpack-bundle-analyzer 我们可以直观的看到打包结果中,文件的体积大小、各模块依赖关系、文件是否重复等问题,极大的方便我们在进行项目优化的时候,进行问题诊断。

安装
代码语言:txt复制
$ npm i -D webpack-bundle-analyze
配置插件
代码语言:txt复制
// 引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

const config = {
  // ...
  plugins:[ 
    // ...
    // 配置插件 
    new BundleAnalyzerPlugin({
      // analyzerMode: 'disabled',  // 不启动展示打包报告的http服务器
      // generateStatsFile: true, // 是否生成stats.json文件
    })
  ],
};
结果分析

每次打包结束后,会自行启动地址为 http://127.0.0.1:8888 的 web 服务,访问地址就可以看到

图片.png图片.png

鼠标hover上去可以看到每个依赖的体积大小,从上往下,表示依赖的嵌套关系

如果,我们只想保留数据不想启动 web 服务,这个时候,我们可以加上两个配置

代码语言:txt复制
new BundleAnalyzerPlugin({
   analyzerMode: 'disabled',  // 不启动展示打包报告的http服务器
   generateStatsFile: true, // 是否生成stats.json文件
})

这样再次执行打包的时候就只会产生 state.json 的文件了,state.json文件会静态的展示打包之后的体积信息

图片.png图片.png

压缩css

安装 optimize-css-assets-webpack-plugin

代码语言:txt复制
$ npm install -D optimize-css-assets-webpack-plugin 

修改 webapck.config.js 配置

代码语言:txt复制
// 压缩css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

const config = {
  optimization: {
    minimize: true,
    minimizer: [
      // 添加 css 压缩配置
      new OptimizeCssAssetsPlugin({}),
    ]
  },
}

压缩js

uglifyjs-webpack-plugin插件在webpack4之后已经不再维护了,现在已经弃用了,取而代之的是具有相同功能的terser-webpack-plugin插件

而webpack5 内置了terser-webpack-plugin 插件,所以我们不需重复安装,直接引用就可以了

代码语言:txt复制
const TerserPlugin = require('terser-webpack-plugin');

const config = {
  // ...
  optimization: {
    minimize: true, // 开启最小化
    minimizer: [
      // ...
      new TerserPlugin({})
    ]
  },
  // ...
}

在生成环境下打包默认会开启 js 压缩,但是当我们手动配置 optimization 选项之后,就不再默认对 js 进行压缩,需要我们手动去配置。

当minimize设置为true,他会告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle。

删除无用的css

purgecss-webpack-plugin 会单独提取 CSS 并清除用不到的 CSS

安装插件

代码语言:txt复制
$ npm i -D purgecss-webpack-plugin

添加配置

代码语言:txt复制
// ...
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin')
const glob = require('glob'); // 文件匹配模式

// ...
function resolve(dir){
  return path.join(__dirname, dir);
}

const PATHS = {
  src: resolve('src')
}

const config = {
  plugins:[ // 配置插件
    // ...
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`, {nodir: true}) // nodir表示不匹配文件夹
    }),
  ]
}

注意:PurgecssWebpackPlugin不能单独使用,必须先安装配置分离css

tree shaking

Tree-shaking意为摇树,作用是剔除没有使用的代码,以降低包的体积,它是基于ES Module 规范来实现的,所以**Tree Shaking 只支持 ESM 的引入方式,不支持其他的引入方式。**

原理

在 CommonJs、AMD、CMD 等旧版本的 JavaScript 模块化方案中,导入导出行为是高度动态,难以预测的,例如:

代码语言:txt复制
if(process.env.NODE_ENV === 'development'){
  require('./bar');
  exports.foo = 'foo';
}

而 ESM 方案则从规范层面规避这一行为,它要求所有的导入导出语句只能出现在模块顶层,且导入导出的模块名必须为字符串常量,这意味着下述代码在 ESM 方案下是非法的:

代码语言:txt复制
if(process.env.NODE_ENV === 'development'){
  import bar from 'bar';
  export const foo = 'foo';

所以,ESM 下模块之间的依赖关系是高度确定的,鉴于此,webpack可以在运行过程中静态分析模块之间的导入导出,确定 ESM 模块中哪些导出值未曾其它模块使用,并将其删除,以此实现打包产物的优化。

配置

生产环境(production)会默认开启tree-shaking,如果想在测试环境开启tree-shaking的话,需要设置

代码语言:txt复制
  optimization: {
    usedExports: true,
  }

除此之外还需要在package.json中设置 sideEffects

代码语言:txt复制
{
"sideEffects": false,
}
sideEffects

sideEffects的值可以为boolean,也可以是一个数组

  • sideEffects 默认为 true, 告诉 Webpack ,所有文件都有副作用,他们不能被 Tree Shaking。
  • sideEffects 为 false 时,告诉 Webpack ,没有文件是有副作用的,他们都可以 Tree Shaking。
  • sideEffects 为一个数组时,告诉 Webpack ,数组中那些文件不要进行 Tree Shaking,其他的可以 Tree Shaking。
sideEffects 对全局 CSS 的影响

当我们将sideEffects设置为false之后,被引入的全局css文件会被treeShaking掉

原因在于:上面我们将 sideEffects 设置为 false 后,所有的文件都会被 Tree Shaking,通过 import 这样的形式引入的 CSS 就会被当作无用代码处理掉。

例如这样的代码,在打包后,打开页面,你就会发现样式并没有应用上,

代码语言:txt复制
reset.css

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html,
body {
  background-color: #eaeaea;
}
代码语言:txt复制
// main.js
import "./styles/reset.css"

为了解决这个问题,可以在 loader 的规则配置中,添加 sideEffects: true ,告诉 Webpack 这些文件不要 Tree Shaking。

代码语言:javascript复制
rules: [

    // 支持加载css文件
    {
      test: /.css/,
      use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader'],
      exclude: /node_modules/,
      include: path.resolve(__dirname, 'src'),
      sideEffects: true,
    },
    {
      test: /.less/,
      use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader', 'less-loader'],
      exclude: /node_modules/,
      include: path.resolve(__dirname, 'src'),
      sideEffects: true,
    }
]

或者指定package.json中的数组

代码语言:txt复制
"sideEffects": ["./src/**/*.less"],

scope hosting

Scope Hoisting开启之前,webpack打包的代码像这样

代码语言:javascript复制
/******/     /* webpack/runtime/compat get default export */
/******/     (() => {
/******/         // getDefaultExport function for compatibility with non-harmony modules
/******/         __webpack_require__.n = (module) => {
/******/             var getter = module && module.__esModule ?
/******/                 () => (module['default']) :
/******/                 () => (module);
/******/             __webpack_require__.d(getter, { a: getter });
/******/             return getter;
/******/         };
/******/     })();

webpack打包出来是一根匿名闭包,modules是一个加载模块数组,webpack_require用来加载模块,当代码量比较多时会生成大量的函数闭包,体积增大,运行时作用域的定义变多,更消耗内存

原因

  • 被webpack转换之后的模块会带上一层包裹
  • import会被转化成 _webpack_require
  • 主要是为了兼容不同的浏览器
开启Scope Hoisting

Scope Hoisting即作用域提升,将所有的模块按照引用顺序排列在一个函数作用域里面,再适当重命名一些函数,通过这种方式可以减少函数声明和内存开销,在生产环境下已经默认开启

删除无用代码

前面说到,使用TerserWebpackPlugin插件我们可以压缩js代码,除此之外,通过配置TerserWebpackPlugin插件,我们可以在打包时删除如console.log这种代码

代码语言:txt复制
minimizer: [
    new TerserWebpackPlugin({
      terserOptions: {
        compress: {
          drop_console: true, //传true就是干掉所有的console.*这些函数的调用.
          drop_debugger: true, //干掉那些debugger;
          //     pure_funcs: ['console.log'], // 如果你要干掉特定的函数比如console.info ,又想删掉后保留其参数中的副作用,那用pure_funcs来处理   }  }
        },
      },
    }),
]

项目链接

https://github.com/AdolescentJou/webpack-base-demo

最后

感谢你能看到这里,本文总结了减少webpack打包体积的几种方法,希望对你有所帮助,之后会陆续更新其他webpack相关的文章,如果能留下你的一个赞,笔者将感激不尽。

参考链接

https://juejin.cn/post/7004297344300777502

https://juejin.cn/post/7023242274876162084#heading-48

https://webpack.docschina.org/configuration/optimization/#optimizationminimize

0 人点赞