「Vant 打包太大」是移动端项目里最常见的一类性能投诉,但多数时候问题不在 JS,而在样式。Vant 按需引入要解决的正是这件事:Vant 4 默认支持 Tree Shaking,未使用的组件 JS 会被构建工具摇掉,可 vant/lib/index.css 一旦整体引入,全量 CSS 就会原样进包。本文以 Vite 为例,把三种引入方式的差别、unplugin-vue-components + VantResolver 的完整配置、产物验证方法和几个高频坑一次讲透。

先搞清 Vant 4 的模块化结构

Vant 4 的 npm 包同时提供 es(ES Module,供打包器 Tree Shaking)与 lib(含全量样式 index.css)两份产物。从 Vant 4.0 起,官方移除了对 babel-plugin-import 的支持,按需引入不再依赖 Babel 插件:

js main.ts
import { createApp } from 'vue';
import { Button, Field, Form } from 'vant';  // 具名导入,JS 层天然可摇树
import 'vant/lib/index.css';                 // 这一行是全量 CSS,无法被摇掉

const app = createApp(App);
app.use(Button).use(Field).use(Form);
app.mount('#app');

换句话说:JS 按需是默认的,CSS 按需才需要额外配置。这也是官方在文档里建议「如果业务对 CSS 体积要求不是特别极致,用常规用法就好」的原因。

引入方式JS 体积CSS 体积配置成本推荐度
全量注册 app.use(Vant)全量全量最低仅原型/演示
常规用法(具名导入 + 全量 CSS)按需全量低★ 多数项目首选
插件自动按需(两个 unplugin)按需按需中对首屏体积敏感的项目

方案 A:常规用法,先把基础打牢

在 <script setup> 中直接导入组件即可,甚至不需要注册;函数式 API 也用具名导入,写法比 Vant 3 干净得多:

vue src/views/Demo.vue
<script setup lang="ts">
import { Button, Field, showToast, showDialog } from 'vant';
const onSubmit = () => showToast('提交成功');
</script>

<template>
  <Button type="primary" @click="onSubmit">提交</Button>
</template>
💡 小技巧:函数式 API(showToast、showDialog、showNotify、showImagePreview)在常规用法下会随全量 CSS 一起带上样式;若切换到插件按需引入,务必同时配置 unplugin-auto-import,否则弹窗可能「有逻辑没样式」。

方案 B:Vite 自动按需引入

步骤 1:安装三个开发依赖

bash 终端
npm i vant
npm i @vant/auto-import-resolver unplugin-vue-components unplugin-auto-import -D

三者的分工很清楚:unplugin-vue-components 扫描模板并自动注册组件,unplugin-auto-import 自动导入脚本里的函数式 API,@vant/auto-import-resolver 则告诉前两者「这个组件对应哪份 Vant 样式」。

步骤 2:配置 vite.config.ts

typescript vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from '@vant/auto-import-resolver';

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({ resolvers: [VantResolver()] }),
    Components({ resolvers: [VantResolver()] }),
  ],
});

步骤 3:删掉手动导入,直接用

vue src/views/Demo.vue
<script setup lang="ts">
// 无需 import:showToast 由 AutoImport + VantResolver 自动注入
const onSubmit = () => showToast('提交成功');
</script>

<template>
  <!-- 无需注册:van-* 标签由 Components 插件自动解析 -->
  <van-button type="primary" @click="onSubmit">提交</van-button>
  <van-field label="备注" placeholder="请输入" />
</template>

此时 main.ts 里不要再写 import 'vant/lib/index.css',也不要 app.use(Vant)——官方明确提示:全量引入与按需引入同时存在会导致代码重复、样式错乱。

ℹ️
多组件库共存
如果项目里还有 Element Plus 等其它组件库,可以在 Components({ resolvers: [VantResolver(), ElementPlusResolver()] }) 里并列配置多个解析器,插件会按标签前缀分别处理。

验证优化是否真的生效

改完配置别急着收工,用构建产物说话最可靠:

bash 终端
npm run build
ls -lh dist/assets/*.css
# 用 rollup-plugin-visualizer 或 vite-bundle-visualizer 看 chunk 构成
npx vite-bundle-visualizer

判断标准有三条:

  • CSS 明显变小:只用到 Button / Field / Toast 的页面,产物里不应出现整套组件样式。
  • 没有重复 chunk:若同时存在全量 CSS 与 resolver 引入的样式,说明 main.ts 还有残留导入。
  • 生成了声明文件:components.d.ts / auto-imports.d.ts 由插件自动生成,把它们加进 tsconfig.json 的 include,否则 TS 会报「找不到名称 showToast」。

顺带把移动端适配挂进 PostCSS

按官方进阶文档,Vant 默认用 px,需要 vw 或 rem 适配时在 postcss.config.js 配置即可。这一步与按需引入互不冲突,但有个容易忽略的前提:

javascript postcss.config.js
module.exports = {
  plugins: [
    // 375 设计稿:Vant 与业务代码分开换算
    require('postcss-mobile-forever')({
      viewportWidth: (file) => (file.includes('vant') ? 375 : 750),
    }),
  ],
};
⚠️ 注意:使用 webpack / postcss-loader 时不要 ignore node_modules,否则 Vant 自带的样式不会被编译,单位换算对组件库完全失效。

常见坑速查

现象原因处理
样式错乱 / CSS 重复全量引入与自动按需混用删掉 import 'vant/lib/index.css' 与 app.use(Vant)
模板里 Vant 组件不渲染只配了 AutoImport,没配 Components两个插件都要配,且都挂 VantResolver()
Toast 有逻辑没样式函数式 API 样式未被解析补 unplugin-auto-import;或显式引入对应组件样式
TS 报找不到 showToast自动生成的 d.ts 未纳入 tsconfig把 auto-imports.d.ts 加入 include
webpack 下插件报错unplugin-vue-components ≥ 0.26.0 的写法变化改用 Components.default({ ... }) 注册
还在用 babel-plugin-importVant 3 时代的旧配置从 babel.config.js 删除,Vant 4 已不支持
⚠️
Nuxt 3 别照抄这份配置
Nuxt 项目直接用官方模块 @vant/nuxt,在 nuxt.config.ts 的 modules 里加一行即可,它已经把函数式组件的样式按需逻辑处理好了。

怎么选:给一个明确建议

如果项目只用了十几个组件、首屏不是极致敏感,常规用法(具名导入 + 全量 CSS)更省心,官方也是这么推荐的;当页面数量多、组件覆盖面窄、且对首屏 CSS 体积有明确指标时,再上 VantResolver 自动按需。优化前先跑一次构建看体积分布,别为了「听起来更优」提前引入构建复杂度。

性能优化的第一步是测量,不是换方案。