谈谈你对Webpack的理解

  • Webpack是一个模块打包工具,可以使用它管理项目中的模块依赖,并编译输出模块所需的静态文件。
  • 它可以很好地管理、打包开发中所用到的HTML,CSS,JavaScript和静态文件(图片,字体)等,让开发更高效。
  • 对于不同类型的依赖,Webpack有对应的模块加载器,而且会分析模块间的依赖关系,最后合并生成优化的静态资源。

Webpack的基本功能有哪些

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
  • 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
  • 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
  • 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
  • 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

Webpack构建过程

  • 初始化编译参数:从配置文件和shell命令中读取与合并参数
  • 开始编译:根据上一步得到的参数初始化Compiler对象,加载所有配置的Plugin,执行对象的 run 方法开始执行编译。
  • 确定入口:从entry里配置的module开始递归解析entry依赖的所有module
  • 编译模块:从入口文件触发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,然后递归本步骤直到所有入口依赖的文件都进行翻译。
  • 完成模块编译:在经过上一步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系图。
  • 输出资源:根据依赖关系图,组装成一个个包含多个模块的Chunk,再把每个Chunk转化成一个单独的文件加入到输出列表,根据配置确定输出的路径和文件名输出。
  • 在整个流程中Webpack会在恰当的时机执行plugin里定义的逻辑。

loader的作用

  • Loader 是webpack中提供了一种处理多种文件格式的机制,因为webpack只认识JS和JSON,所以Loader相当于翻译官,将其他类型资源进行预处理。即对模块的”源代码”进行转换。
  • loader支持链式调用,原型调用的顺序是从右往左。原型链中的每个loader会处理之前已处理过的资源,最终变为js代码。
  • 可以通过 loader 的预处理函数,为 JavaScript 生态系统提供更多能力。

有哪些常见的Loader

  • optimize-css-assets-plugin:压缩css;
  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
  • url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS
  • json-loader: 加载 JSON 文件(默认包含)
  • babel-loader:把 ES6 转换成 ES5
  • ts-loader: 将 TypeScript 转换成 JavaScript
  • less-loader:将less代码转换成CSS
  • eslint-loader:通过 ESLint 检查 JavaScript 代码
  • vue-loader:加载 Vue单文件组件

Plugin的作用

  • Plugin功能更强大,主要目的就是解决loader 无法实现的事情,比如打包优化和代码压缩等。
  • Plugin加载后,在webpack构建的某个时间节点就会触发plugin定义的功能,帮助webpack做一些事情。实现对webpack的功能扩展。

有哪些常见的Plugin

html-webpack-plugin

用途:自动创建一个 HTML 文件,并将打包好的 JavaScript 文件插入到这个 HTML 文件中。

const HtmlWebpackPlugin = require('html-webpack-plugin');
 
module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html', // 使用现有的 HTML 文件作为模板
      filename: 'index.html', // 输出的 HTML 文件名
      inject: 'body' 
  })
  ]
};

uglifyjs-webpack-plugin

用途:压缩 JavaScript 文件。需要注意的是,UglifyJS 不支持 ES6+ 代码压缩。如果你需要压缩 ES6+ 代码,可以考虑使用 terser-webpack-plugin

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
 
module.exports = {
  optimization: {
    minimizer: [new UglifyJsPlugin()]
  }
};

mini-css-extract-plugin

用途: 在构建过程中将 CSS 代码从 JavaScript 打包文件中抽离出来,单独生成一个或多个 CSS 文件。这样可以提高页面加载性能,因为浏览器可以并行加载 CSS 和 JavaScript 文件。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
 plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css'
    })
  ]
};

clean-webpack-plugin

用途: 在每次构建之前清理输出目录,删除整个输出文件夹下的内容。这有助于避免旧的文件干扰新的构建结果,确保每次构建都是基于最新的源代码。

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 
module.exports = {
  plugins: [
    new CleanWebpackPlugin()
  ]
};

copy-webpack-plugin

用途: 用于将项目中的文件或文件夹复制到构建输出目录中。

const CopyWebpackPlugin = require('copy-webpack-plugin');
 
module.exports = {
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        { from: 'src/assets', to: 'assets' } // 将 src/assets 文件夹复制到输出目录的 assets 文件夹中
      ]
    })
  ]
};

webpack-bundle-analyzer

用途: 可视化 Webpack 输出文件的体积,帮助你分析业务组件和依赖的第三方模块的大小。

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
 
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

optimize-css-assets-plugin

用途: 压缩 CSS 文件,减少文件大小。

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 
module.exports = {
  optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin()]
  }
};

Loader和Plugin的区别

  • Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。
  • Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
  • Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
  • Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。

Webpack如何配置压缩代码?压缩了什么?

  • js压缩:利用terser-webpack-plugin
  • css压缩:利用了optimize-css-assets-webpack-plugin 插件
  • 删除了console、注释、空格、换行、没有使用的css代码等

如何优化 Webpack 的构建速度

  • 使用高版本的 Webpack 和 Node.js
  • 压缩代码:通过 uglifyjs-webpack-plugin 压缩JS代码,通过 mini-css-extract-plugin 提取 chunk 中的 CSS 代码到单独文件,通过 css-loader 的 minimize 选项开启 cssnano 压缩 CSS。
  • 多线程/多进程构建:thread-loader, HappyPack
  • 压缩图片: image-webpack-loader
  • 缩小打包作用域:exclude/include、resolve.modules、resolve.mainFields、resolve.extensions、noParse、ignorePlugin

Webpack 的热更新原理

参考

https://blog.csdn.net/weixin_44116302/article/details/137911381

  • Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
  • HMR的核心就是客户端从服务端拉去更新后的文件,准确地说是 chunk diff (chunk 需要更新的部分),实际上 WDS 与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新。
  • 后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR。

什么是bundle?什么是chunk?什么是module?

  • bundle:是webpack打包后的一个文件;
  • chunk:代码块,一个chunk 可能有很多的模块组成,用于合并和分割代码;
  • module:模块,在webpack中,一切都是模块,一个文件就是一个模块,她从入口开始查找webpack依赖的所有模块

package.json中sideEffects的作用

sideEffects和 Tree Shaking实际上二者没什么因果关系。

sideEffects用于告知打包工具(如 Webpack、Rollup 等)哪些模块或文件可能存在副作用(side effects),以便在进行 Tree Shaking(摇树优化)时做出正确决策。

Tree Shaking 是一种优化技术,旨在在打包过程中去除那些在最终代码中未被引用的模块,以减小打包后的文件大小。它依赖于 ES6 模块的静态导入导出特性,能够分析模块间的依赖关系,丢弃未使用的代码。

然而,某些模块可能包含副作用,即除了导出可供外部使用的值之外,还在模块内部执行了其他操作,如全局变量赋值、注册事件监听器、修改外部状态等。这些副作用可能在模块被导入时触发,即使导入者并未直接使用模块导出的任何值。为了防止误删此类模块,需要在 sideEffects 字段中明确指出。

webpack优化性能

  • 升级插件(Rush编译的,提升性能)
  • 去除冗余npm包(功能重复的、不用的等等)
  • 配置external,把npm排除编译