巧用 resolve.alias 优化构建产物:消除重复打包与提升 Tree Shaking 效果

统一资源引用路径

使用 webpack-bundle-analyzer 分析umi项目的构建产物,突然从分析界面中发现。es 和 lib中的文件都被打包进来,并且有很多重复文件。

查看 package.json 文件,我们可以看到,lib目录是 commonjs 产物, es目录是 es module 产物,且专门声明了 "sideEffects": false 来启用webpack的 tree shaking。 逐一分析了几个依赖了@ant-design/icons的组件仓库,找到了问题所在: 从绝对路径的图标代码文件中导入的,有 es 也有 lib。

js 复制代码
import { createFromIconfontCN } from '@ant-design/lib/icons';
import { createFromIconfontCN } from '@ant-design/es/icons';

想要从源头解决这个问题,其实是很难的。我们不可能把这么多组件中所有的import都去改一遍,也没办法限制用户使用同一种方式去导入图标。所以只能想办法去修正这些引用路径,统一从一个地方去导入。

webpack的 resolve.alias 配置项可以让开发者给模块设置导入时的别名,从而用一个简短的别名来代替长路径的导入。

我们可以借助这一特性来将所有的 @ant-design/icons/lib 路径替换为 @ant-design/icons/es,这样整个图标都可以从 es 目录引入,从而避免代码被重复打包。

arduino 复制代码
// config/config.ts 或 umirc.ts
export default defineConfig({
  alias: {
    '@ant-design/icons/lib': '@ant-design/icons/es',
  },
})

经过如上改造后,图标库体积从83kb降到了47kb,重复代码减少了43%。

拯救lodash等无法被tree shaking的包

lodash 这个工具包大家一定不陌生,但你知道lodash并不支持esm吗?是的,它目前只支持了commonjs模块语法,所以并不能天然被 tree shaking。

json 复制代码
{
  "name": "lodash",
  "version": "4.17.21",
  "main": "lodash.js"
}

目前,常用的 lodash 代码精简手段包括:

  • 手动按需引入 import merge from 'lodash/merge'
  • 手动单个函数模块引入 import merge from 'lodash.merge'
  • 使用 esm 版本的 lodash-esimport { merge } from 'lodash-es'
  • 使用 babel-plugin-lodash 将全量引入的lodash转换为按需引入的形式

另外还有一个小妙招,那就要请出本篇文章的主角 alias 了。如下所示,我们直接把 lodash 的别名配置为 lodash-es

php 复制代码
export default defineConfig({
  alias: {
    lodash: 'lodash-es',
  },
})

这样配置后,当代码中出现 import { merge } from 'lodash'; 这样的语句时,webpack 会将其解析为 import { merge } from 'lodash-es';,从而使 tree shaking 生效。

修改前,代码体积是111kb,修改后,代码体积仅有42kb,减少了69kb。

风险提示

  • 兼容性问题
    项目中可能存在多个第三方依赖同时使用了 lodash 的情况,各自使用的版本也各不相同。强行把这些不同版本的 lodash 替换为我们安装的 lodash-es,可能会出现不兼容的情况。
  • 构建工具和插件
    某些构建工具或插件可能对 lodash 有特定的优化,而这些优化可能不适用于 lodash-es。

常规用法

resolve.alias 在项目中有几个实际的好处:

  1. 简化模块引用 :当你的项目结构变得复杂时,你可能需要频繁地使用相对路径去引用模块,例如import '../../../utils/my-util'。通过设置别名,你可以简化这个引用,例如import 'Utils/my-util'
  2. 提高可维护性:如果你决定改变某个模块的位置,通常你需要更新所有引用该模块的文件中的路径。使用别名可以让你只在webpack配置中更新一次路径。
  3. 避免相对路径混乱 :在大型项目中,过多的相对路径(如../../..)会使代码难以理解和维护。别名提供了一种清晰的方式来表示模块的位置。
  4. 模块解析优化:在某些情况下,你可能想要针对不同的环境或目标指定不同的模块实现。通过别名,你可以在构建时决定使用哪个模块版本。
  5. 与第三方库集成:有时候,你可能需要覆盖第三方库中的某些文件或模块。通过设置别名,你可以重定向这些导入到你自己的实现中。
  6. 支持模块重构:当你重构项目,将代码分割成不同的包或模块时,别名可以帮助你更平滑地过渡,因为你可以保持导入语句不变,即使文件的实际位置已经改变。

下面是一个resolve.alias的配置示例:

java 复制代码
module.exports = {
  // ...
  resolve: {
    alias: {
      '@components': path.resolve(__dirname, 'src/components/'),
      '@utils': path.resolve(__dirname, 'src/utils/'),
      // 可以指定特定文件的别名
      'module-to-mock': path.resolve(__dirname, 'mocks/module-to-mock.js')
    }
  }
};

在这个配置中,任何时候你导入@components@utils,webpack都会自动解析到对应的目录。这样,你就不需要写出完整的相对或绝对路径,从而使得代码更加简洁和易于维护。

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax