巧用 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都会自动解析到对应的目录。这样,你就不需要写出完整的相对或绝对路径,从而使得代码更加简洁和易于维护。

相关推荐
程序视点3 小时前
IObit Uninstaller Pro专业卸载,免激活版本,卸载清理注册表,彻底告别软件残留
前端·windows·后端
前端程序媛-Tian3 小时前
【dropdown组件填坑指南】—怎么实现下拉框的位置计算
前端·javascript·vue
嘉琪0013 小时前
实现视频实时马赛克
linux·前端·javascript
烛阴4 小时前
Smoothstep
前端·webgl
若梦plus4 小时前
Eslint中微内核&插件化思想的应用
前端·eslint
爱分享的程序员4 小时前
前端面试专栏-前沿技术:30.跨端开发技术(React Native、Flutter)
前端·javascript·面试
超级土豆粉4 小时前
Taro 位置相关 API 介绍
前端·javascript·react.js·taro
若梦plus4 小时前
Webpack中微内核&插件化思想的应用
前端·webpack
若梦plus4 小时前
微内核&插件化设计思想
前端
柯北(jvxiao)4 小时前
搞前端还有出路吗?如果有,在哪里?
前端·程序人生