网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


前言

在实际项目开发中,前端构建的坑经常出现在一些意想不到的地方。这次我在做 Vue3 + Vant 项目打包的时候,遇到了一个让人摸不着头脑的报错:

[vite:legacy-post-process] unknown: Identifier 'bem' has already been declared

按理说这是个语法错误,但奇怪的是项目本地开发完全没问题,只有在 vite build 打包时才会报错。这里我记录下完整的排查和解决过程,也分享一些在工程化场景下的经验。

问题复现

我项目的基本依赖如下:

{
  "dependencies": {
    "vue": "^3.3.4",
    "vant": "^4.8.0"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "@vitejs/plugin-vue": "^5.0.0",
    "@vitejs/plugin-legacy": "^5.4.0"
  }
}

代码中使用了 Vant 按需引入,配置方式如下:

// vite.config.ts
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import legacy from "@vitejs/plugin-legacy"
import Components from "unplugin-vue-components/vite"
import { VantResolver } from "unplugin-vue-components/resolvers"

export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [VantResolver()],
    }),
    legacy({
      targets: ["defaults", "not IE 11"],
    }),
  ],
})

运行 npm run dev 一切正常,UI 渲染没问题。但一旦 npm run build,就抛出:

[vite:legacy-post-process] unknown: Identifier 'bem' has already been declared

而且注释掉 legacy 插件,依然会报错。

排查过程

  1. 怀疑是 Vant 内部工具函数冲突
    Vant 内部实现里确实有一个 bem 方法,用来生成 CSS BEM className。但按理说不会暴露到全局,也不该和我们项目的变量冲突。

  2. 定位到编译产物
    我在打包后的 .vite 缓存和 dist 下代码里搜索 bem,发现编译结果里有重复的 const bem = ... 定义,说明某些模块在打包时被重复引入,导致语法层面报错。

  3. 怀疑 unplugin-vue-components 插件引入方式
    如果 unplugin-vue-components 插件没有正确配置 resolvers,有可能导致 Vant 组件被同时按需引入和全量引入,进而重复打包。

  4. 对比官方文档
    Vant 官方建议 Vite 项目用 unplugin-vue-components + unplugin-auto-import,而且要保证 Vant 只走一套导入机制。

解决方案

最终我通过以下几个步骤解决了问题:

1. 确认 Vant 引入方式唯一

先检查自己代码里有没有手动 import { Button } from 'vant' 或者 import 'vant/lib/index.css'。如果有,可能会和 unplugin-vue-components 插件的自动导入冲突。
最终只保留插件方式:

// vite.config.ts
import Components from "unplugin-vue-components/vite"
import { VantResolver } from "unplugin-vue-components/resolvers"

Components({
  resolvers: [VantResolver()],
})

代码中直接写:

<template>
  <van-button type="primary">确认</van-button>
</template>

不再写任何 import。

2. 排查 legacy 插件冲突

虽然注释掉 legacy 还是报错,但在我的场景里,legacy 会进一步放大 polyfill 和语法转换的问题。我尝试了 升级 legacy 插件 并修改配置:

legacy({
  targets: ["defaults", "not IE 11"],
  additionalLegacyPolyfills: ["regenerator-runtime/runtime"], // 补充必要 polyfill
})

这样可以避免某些全局 polyfill 影响。

3. 锁定依赖版本

因为 bem 冲突和 Vant 内部实现相关,我最终把 Vant 锁定到 最新的稳定版本,并清理 node_modules 重新安装:

rm -rf node_modules package-lock.json pnpm-lock.yaml
npm install

我的最终依赖:

"vant": "4.8.5",
"unplugin-vue-components": "^0.25.2",

可运行 Demo

这是一个最小可复现的 Demo 配置:

npm init vite@latest vite-vant-demo
cd vite-vant-demo
npm install
npm install vant unplugin-vue-components unplugin-auto-import

vite.config.ts 配置:

import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import Components from "unplugin-vue-components/vite"
import { VantResolver } from "unplugin-vue-components/resolvers"

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

App.vue:

<template>
  <van-button type="primary">确认</van-button>
</template>

运行 npm run build,就不会再出现 Identifier 'bem' has already been declared 的报错。

实际场景的启示

这个问题表面看是 “语法错误”,但实质是 重复依赖和编译冲突。我总结几点经验:

  1. 依赖引入保持单一来源:按需引入就不要再手动 import,否则容易重复打包。
  2. 插件版本要匹配:Vite 插件、Vant、unplugin-vue-components 要保持在相互兼容的版本。
  3. legacy 插件要谨慎使用:如果目标用户浏览器环境允许,可以考虑不引入 legacy,以避免构建复杂化。
  4. 遇到奇怪语法报错,直接翻 dist 代码:通常能看到冲突的根源。

总结

Vue3 + Vant 项目打包时报 Identifier 'bem' has already been declared,多数是由于 组件引入重复插件版本不兼容
解决方式是统一引入方式(推荐 unplugin-vue-components)、升级到最新 Vant 版本、必要时调整 legacy 配置。

这类问题虽然定位过程比较绕,但一旦搞清楚构建流程,就能快速解决。

Logo

加入「COC·上海城市开发者社区」,成就更好的自己!

更多推荐