Skip to content

webpack5

本文介绍 webpack5 和 webpack4 的区别

一、性能优化

1. 持久缓存

Webpack 5:内置文件系统缓存,大幅提升二次构建速度;Webpack 4:需使用 cache-loader 或第三方插件实现。

js
// webpack.config.js
module.exports = {
  cache: {
    type: 'filesystem', // 启用文件系统缓存
    buildDependencies: {
      config: [__filename] // 配置文件变化时重新缓存
    }
  }
};

2. 优化 Tree Shaking

Webpack 5:默认启用更严格的 Tree Shaking,移除未使用代码;Webpack 4:需手动配置。

  • 更精确地识别和移除未使用代码(基于 ESM 静态分析)。
  • 支持通过 sideEffects 标记无副作用的模块。

3, 并行处理

Webpack 5:内置支持多线程构建;Webpack 4:需使用第三方插件(如 happypack)。

二、 内置了资源处理

使用 Asset Modules 替代 file-loader、url-loader 和 raw-loader,简化资源处理:

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/,
        type: 'asset/resource', // 输出文件
      },
      {
        test: /\.svg$/,
        type: 'asset/inline', // 内联为 Base64
      },
      {
        test: /\.txt$/,
        type: 'asset/source', // 导出源代码
      },
      {
        test: /\.jpg$/,
        type: 'asset', // 自动选择导出文件或内联
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024 // 4KB 以下内联
          }
        }
      }
    ]
  }
};

三、模块联邦

Webpack 5 引入 Module Federation,支持微前端架构和动态加载远程模块:

1. 配置说明

js
// 远程应用配置
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'remoteApp',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button'
      }
    })
  ]
};

// 宿主应用配置
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'hostApp',
      remotes: {
        remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js'
      }
    })
  ]
};

// 使用
import Button from 'remoteApp/Button';
//...

2. 可能的坑

  1. 版本冲突:确保远程模块和宿主应用使用相同的依赖版本。

    • 问题:远程和宿主应用使用同一依赖的不同版本,导致运行时错误(如 React Hooks 报错)。
    • 解决:使用共享依赖配置,确保版本一致。
    js
     new ModuleFederationPlugin({
       shared: {
          react: { singleton: true, requiredVersion: '^17.0.2' },
          'react-dom': { singleton: true, requiredVersion: '^17.0.2' }
       }
     });
  2. 性能问题:远程模块体积过大或加载时机不当,影响首屏性能。

    • 动态加载:确保远程模块在需要时才加载,避免影响初始加载时间。
    jsx
    // 懒加载非关键模块
    const RemoteComponent = React.lazy(() => import('remoteApp/Component'));
    
    // Suspense 提供加载状态
    <React.Suspense fallback={<Spinner />}>
      <RemoteComponent />
    </React.Suspense>
    • 增加监控,确定远程模块加载时间和性能指标。
  3. 调试问题:远程模块调试可能不如本地模块方便。

    • 解决:确保远程模块生成 Source Map,并在开发环境中使用
    js
    module.exports = {
     devtool: 'source-map', // 确保生成 Source Map
     // ...
    };
  4. 全局变量或样式冲突

    • 样式冲突问题

      • 问题:远程模块可能引入全局样式或变量,导致宿主应用样式错乱。
      • 解决:避免编写全局样式,使用 CSS Modules 或 Scoped CSS,避免全局污染。
    • 全局变量冲突

      • 问题:远程模块可能修改全局变量(如 window 对象),导致宿主应用异常。
      • 解决:约定使用命名空间,避免直接修改全局变量。
  5. 安全问题

    • 问题:远程模块可能被篡改
    • 解决办法:script 标签的 integrity 属性(Subresource Integrity, SRI)通过验证外部资源的完整性来防止中间人攻击和 CDN 被篡改的风险
  6. 管理问题

    • 问题:怎么管理不同远程模块的版本和依赖?
    • 解决办法:推荐特定场景使用。如低代码平台,共享不同的远程模块,不存在管理问题。
    • 如果简单使用可以,则分享的模块需要上CDN版本化链接。