React + Vite 实现 antd 组件按需自动引入

React + Vite 实现 antd 组件按需自动导入

最近在写一个 React + TypeScript + Vite 的后台管理系统时遇到了一些问题。

ts 复制代码
import { 
  Button, 
  Input, 
  InputNumber, 
  Card, 
  Form, 
  Row, 
  Col, 
  Switch, 
  Badge, 
  Dropdown, 
  Divider
} from 'antd'

相信大家对上述代码一定不陌生,当组件中引入大量 antd 组件时,我们不得书写一个如此长的 import 声明。

这些声明在代码里极为啰嗦的,尤其是搭配 Prettier 的时候,当每行的文本数超过 Prettier 的设定时,Prettier 会帮你自动换行,导致一个 import 声明就长达十几或二十几行。

如何避免这些繁琐的声明,且又能享受到 ES Module 静态引入的 Tree Shaking 呢?

unplugin-auto-import

在 Vite 中,我们可以使用 unplugin-auto-import 这个插件来解决上述问题。这个插件可以帮我们自动导入任何常量、函数、类、组件等。

如果你使用的是 Vue,可以搭配 unplugin-vue-components 来实现 Vue 组件的自动引入。

关于插件的使用方法详细请看 unplugin-auto-importunplugin-vue-components 的文档,本文不再赘述。

在 React 中使用

unplugin-auto-import 官方为我们提供了许多预设配置,如 react、react-router 等,这些预设这边称之为 "静态自动引入"。

所谓 "静态自动引入",是因为项目启动后,会根据这些预设生成自动引入的声明,即便你没有在项目中使用如 useLayoutEffect 这样的 Hook,插件也会自动生成该声明。

通常这样的预设比较适合的场景为:

  • 包比较小,无须考虑打包后文件大小的问题
  • 使用率较高,如 useStateuseEffect 这样的 Hook,几乎每个项目都会使用到

但是像 antd 这样的组件库,通常都导出了大量的组件,而很多组件通常是项目用不到的,此时 "静态自动引入" 就不合适了,我们需要使用一种 "动态自动引入" 的方法来解决问题。

值得庆幸的是,unplugin-auto-import 提供了 resolver(解析器) 来帮助我们实现 "动态自动引入"。

unplugin-auto-import-antd

为了实现自动引入 antd,我写了一个叫 unplugin-auto-import-antd 的解析器,已在 GitHub 开源。

特性

  • 支持 Vite, Webpack
  • 支持自动引入 antd 组件
  • 支持使用自定义前缀重命名组件
  • 支持通过包的别名引入

仅支持 antd v5+.

安装

npm 安装

bash 复制代码
npm i -D unplugin-auto-import-antd unplugin-auto-import

yarn 安装

bash 复制代码
yarn add -D unplugin-auto-import-antd unplugin-auto-import

pnpm 安装

bash 复制代码
pnpm add -D unplugin-auto-import-antd unplugin-auto-import

bun 安装

bash 复制代码
bun add -D unplugin-auto-import-antd unplugin-auto-import

使用

Vite

ts 复制代码
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import AntdResolver from 'unplugin-auto-import-antd'

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [AntdResolver()]
    })
  ]
})

Webpack

js 复制代码
// webpack.config.js
const AntdResolver = require('unplugin-auto-import-antd')

module.exports = {
  /* ... */
  plugins: [
    require('unplugin-auto-import/webpack')({
      resolvers: [AntdResolver()]
    })
  ]
}

自定义前缀

ts 复制代码
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import AntdResolver from 'unplugin-auto-import-antd'

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [
        AntdResolver({
          prefix: 'A'
        })
      ]
    })
  ]
})

使用自定义前缀,如 A, 书写组件的方式有原本的 Button 变为 AButton

等价于 import { Button as AButton } from 'antd'

包别名引入

ts 复制代码
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import AntdResolver from 'unplugin-auto-import-antd'

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [
        AntdResolver({
          packageName: 'antd-v5'
        })
      ]
    })
  ]
})

通过别名安装 antd,如 antd-v5。

等价于 import { Button } from 'antd-v5'

如何写一个解析器

定义预设

我们需要定义一个数组,用于存放所有 antd 导出组件的名称,解析器是通过将未声明的变量与这个数组进行匹配,来判断是否要生成一个自动引入的声明。

ts 复制代码
// antd 内置组件
export const antdBuiltInComponents = [
  'Affix',
  'Alert',
  'Anchor',
  'App',
  'AutoComplete',
  'Avatar',
  'BackTop',
  'Badge',
  'Breadcrumb',
  'Button',
  'Calendar',
  'Card',
  'Carousel',
  'Cascader',
  'Checkbox',
  'Col',
  'Collapse',
  'ColorPicker',
  'ConfigProvider',
  'DatePicker',
  'Descriptions',
  'Divider',
  'Drawer',
  'Dropdown',
  'Empty',
  'Flex',
  'FloatButton',
  'Form',
  'Grid',
  'Image',
  'Input',
  'InputNumber',
  'Layout',
  'List',
  'Mentions',
  'Menu',
  'message',
  'Modal',
  'notification',
  'Pagination',
  'Popconfirm',
  'Popover',
  'Progress',
  'QRCode',
  'Radio',
  'Rate',
  'Result',
  'Row',
  'Segmented',
  'Select',
  'Skeleton',
  'Slider',
  'Space',
  'Spin',
  'Statistic',
  'Steps',
  'Switch',
  'Table',
  'Tabs',
  'Tag',
  'theme',
  'TimePicker',
  'Timeline',
  'Tooltip',
  'Tour',
  'Transfer',
  'Tree',
  'TreeSelect',
  'Typography',
  'Upload',
  'Watermark'
]

工具函数

这边工具函数主要是支持引入时添加前缀的功能,getAntdComponentsMap 将上述数组转化为形为 [带前缀的组件名,antd 导出的组件名] 的Map。这便于解析器通过我们的带前缀的组件名,找到 antd 原始导出的组件名,然后根据该映射创建别名导入。

同时我们注意到并非所有 antd 导出的都是大陀峰命名的组件,像 thememessagenotification 这样的导出,我们要进行特殊处理,将其转换为大陀峰格式。

ts 复制代码
import { antdBuiltInComponents } from './preset'

// 处理特殊的组件名(非大驼峰)
export const getComponentName = (name: string) => {
  if (name === 'theme') {
    return 'Theme'
  }
  if (name === 'message') {
    return 'Message'
  }
  if (name === 'notification') {
    return 'Notification'
  }
  return name
}

// 获取添加前缀后的组件名映射
export const getAntdComponentsMap = (prefix?: string): Map<string, string> =>
  antdBuiltInComponents.reduce(
    (map, name) => map.set(`${prefix ?? ''}${getComponentName(name)}`, name),
    new Map()
  )

解析器函数

解析器函数实现很简单,会返回一个包含 typeresolve 的对象。type 我们指定为 component 即组件。resolve 是解析函数,接受我们在代码中定义的未声明的变量名,返回一个用于自动导入的对象。

  • from: 从 xxx 包导入,如 from 'antd'
  • name: 导入的原生名称,如 import { Button }
  • as: 别名导入,如 import { Button as AButton} from 'antd'
ts 复制代码
import type { Resolver } from 'unplugin-auto-import/types'

import { antdBuiltInComponents } from './preset'
import type { AntdResolverOptions } from './types'
import { getAntdComponentsMap } from './utils'

export const antdResolver = (options: AntdResolverOptions = {}): Resolver => {
  const { prefix, packageName: from = 'antd' } = options
  const antdComponentsMap = getAntdComponentsMap(prefix)
  return {
    type: 'component',
    resolve: (originName: string) => {
      if (!prefix) {
        if (antdBuiltInComponents.includes(originName)) {
          return {
            from,
            name: originName
          }
        }
      } else {
        // 如果设定前缀,则重命名引入
        const name = antdComponentsMap.get(originName)
        if (name) {
          return {
            from,
            name,
            as: originName
          }
        }
      }
      return undefined
    }
  }
}

总结

可以自由设定预设集合,或使用一种特殊的方法去匹配需要解析器动态导入的组件(如 Naive UI 组件默认都以 N 开头),无需拘泥于本文的实现,本文仅提供一个实现解析器的思路。

其他

最近在开发一个叫 Dolphin Admin 的后台管理系统,目前在 GitHub 开源。

目前预览版只有 Vue 版本,React 版本还在缓慢进行中。

有兴趣的可以 🌟 一下,到时候会出详细的文档,包括后端(基于 Nest)。

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