Skip to content

Webpack

Webpack是一个强大的模块打包工具。它的主要作用是将各种模块,比如JS、CSS、图片等,进行打包和优化,以提高前端项目的 性能可维护性。他可以:

  • 处理模块的依赖关系
  • 压缩代码
  • 生成雪碧图等等

基本配置示例

运行这个配置,它会将 src/index.js 作为入口文件,处理其中引入的 CSS 和图片等资源,并将最终打包的结果输出到 dist 文件夹中的 bundle.js 文件。

js
module.exports = {
  entry: './src/index.js', // 入口文件,
  output: {
    filename: 'bundle.js', // 打包输出的文件名,
    path: path.resolve(__dirname, 'dist') // 输出路径
  },
  module: {
    rules: [
      {
        test: '/\.css$/', // 匹配.css文件
        use: ['style-loader', 'css-loader'] // 使用的loader处理css
      },
      {
        test: /\.(png|svg|jpg|gif)$/,  // 匹配图片文件
        use: ['file-loader']  // 使用的 loader 处理图片
      }
    ]
  }
}

为什么要引入loader

Webpack主要是用于处理Javascript的,本身不能直接处理 CSS 和图片等 非JavaScript 资源。

CSS loader可以让webpack理解和处理CSS文件,将其转换为webpack能够处理的模块形式并打包。使用file loader则是为了处理图片等文件,将它们复制到输出目录,并返回处理后的路径,以便在代码中正确引用。

常用的插件及作用

webpack中常用的插件有:

  • HtmlWebpackPlugin:用于生成 HTML 文件,并自动引入打包后的资源。
  • CleanWebpackPlugin:在每次构建前清理输出目录,确保输出目录干净。
  • MiniCssExtractPlugin:将 CSS 从 JavaScript 中提取为独立的 CSS 文件。
  • CopyWebpackPlugin:用于复制静态文件到输出目录。

如何优化webpack的打包速度

  1. 合理配置entryouput,减少不必要的模块打包

    • 比如,如果某个模块在特定的页面或功能中才使用,就不应该在通用的入口文件中引入
  2. 开启缓存,如babel-loader的缓存

    • 像 babel-loader 这样的工具在处理代码时可能会消耗大量时间
    • 开启缓存后,如果文件内容没有变化,就直接使用之前的处理结果,而不需要重新进行转换和处理,从而提高构建速度。
  3. 缩小文件搜索范围,例如通过resolve.modules和resolve.extensions配置

    • resolve.modules:指定 Webpack 查找模块的目录。可以明确指定优先搜索的目录,避免在不必要的地方搜索模块,节省时间。
    • resolve.extensions:指定在导入模块时可以省略的文件扩展名。例如,如果只使用 .js 和 .json 文件,可以将其配置进去,这样 Webpack 就不会尝试去匹配其他不相关的扩展名,提高模块解析的效率
  4. 利用 DllPluginDllReferencePlugin 分离第三方库,避免每次构建都重新打包

    • 一些大型项目会依赖很多第三方库,如 jQuery、React 等。这些库通常不经常变化
    • DllPlugin 用于将第三方库提前打包成一个单独的动态链接库(DLL)文件。
    • DllReferencePlugin 则在主构建中引用这个提前打包好的 DLL 文件。这样,在后续的构建中,只要第三方库没有更新,就不需要重新打包它们,大大节省了构建时间。

代码分割

代码分割(Code Splitting)是将一个大型的 JavaScript 应用拆分成多个小的代码块chunk,只在需要的时候加载特定的代码块,从而提高应用的初始加载速度和性能。

可以通过以下方式实现代码分割:

  1. 使用splitChunks配置进行公共模块的提取

  2. 动态导入(import()),在需要的时候按需加载模块

js
// 例如在某个组件中
function SomeComponent() {
  if (someCondition) {
    import('./SomeModule').then(module => {
      // 使用导入的模块
    });
  }
}

在上述代码中,import('./SomeModule') 会根据条件按需加载 SomeModule 模块,而不是在应用初始加载时就加载。

当用户不一定会触发某些功能或访问某些页面时,相关的代码不会在初始加载时就加载进来,减少了初始加载的时间和资源消耗。

与其他打包工具的比较

  • Webpack 功能强大,配置灵活但相对复杂。
  • Parcel 配置简单,上手快,性能也不错,但灵活性稍逊。
  • Rollup 更适合打包库,能生成更小、更高效的代码。

Webpack 适合大型复杂项目,Parcel 适合小型项目快速开发,Rollup 则在库开发方面有优势。

热模块替换HMR

当代码发生更改时,Webpack 会监测到变化,并尝试只更新发生改变的模块,而不是重新加载整个页面。

具体来说,Webpack 会在开发服务器和应用之间建立一个 WebSocket 连接。当模块更新时,Webpack 会通过这个连接将更新的模块信息发送到客户端。客户端接收到更新信息后,会尝试使用新的模块代码替换旧的模块代码,同时保持应用的当前状态,比如组件的实例、用户输入等不变。

在 Webpack 配置中启用热模块替换(HMR):

首先是 Webpack 配置(webpack.config.js)

js
const path = require('path');

module.exports = {
  //... 其他配置

  devServer: {
    hot: true,  // 启用 HMR
    contentBase: path.resolve(__dirname, 'dist'),  // 静态文件的根目录
    port: 8080  // 服务端口
  },

  //... 其他配置
};

然后,在模块代码中(例如一个简单的 JavaScript 文件)添加对 HMR 的支持:

js
if (module.hot) {
  module.hot.accept();  // 接受模块的更新
}

module.hot 是 Webpack 在运行时注入到模块中的对象,用于支持热模块替换(HMR)的相关操作。