jeecgboot/JeecgBoot · error
【addComponent】组件"${type}"已存在
Error message
【addComponent】组件"${type}"已存在 What it means
addComponent stores components in a module-level Map keyed by JVxeTypes. If a type is already present in componentMap, re-registering throws to prevent silent shadowing of an existing cell. This typically surfaces during hot-module replacement (HMR): the registration module re-executes on save and tries to re-add already-registered components. The code has an issues/860 workaround that assigns the map to window in DEV for HMR recovery, but the duplicate guard itself still throws on a plain re-import.
Source
Thrown at jeecgboot-vue3/src/components/jeecg/JVxeTable/src/componentMap.ts:50
JVxeTypes.rowNumber,
JVxeTypes.rowCheckbox,
JVxeTypes.rowRadio,
JVxeTypes.rowExpand,
];
/**
* 注册组件
*
* @param type 组件 type
* @param component Vue组件
* @param spanComponent 显示组件,可空,默认为 JVxeNormalCell 组件
*/
export function addComponent(type: JVxeTypes, component: JVxeVueComponent, spanComponent?: JVxeVueComponent) {
if (excludeKeywords.includes(type)) {
throw new Error(`【addComponent】不能使用"${type}"作为组件的name,因为这是关键字。`);
}
if (componentMap.has(type)) {
throw new Error(`【addComponent】组件"${type}"已存在`);
}
componentMap.set(type, component);
if (spanComponent) {
componentMap.set(type + spanEnds, spanComponent);
}
// 代码逻辑说明: 【issues/860】生成的一对多代码,热更新之后点击新增卡死[暂时先解决]
import.meta.env.DEV && (window[JVxeComponents] = componentMap);
}
export function deleteComponent(type: JVxeTypes) {
componentMap.delete(type);
componentMap.delete(type + spanEnds);
// 代码逻辑说明: 【issues/860】生成的一对多代码,热更新之后点击新增卡死[暂时先解决]
import.meta.env.DEV && (window[JVxeComponents] = componentMap);
}
/** 定义内置自定义组件 */
export function definedComponent() {View on GitHub (pinned to 96fb33f5ec)
Solutions
- Guard the registration with a componentMap.has(type) check before calling addComponent, or call deleteComponent first.
- For HMR, ensure the registration runs once per module load and is idempotent (wrap in a flag or use the existing has-check).
- Eliminate double-import paths: register each type in exactly one module.
- If a plugin re-registers, move registration to app.initializeApp, not per-component install.
Example fix
// before — throws on HMR re-evaluation
addComponent(JVxeTypes.input, JVxeInputCell);
// after — idempotent registration
if (!componentMap.has(JVxeTypes.input)) {
addComponent(JVxeTypes.input, JVxeInputCell);
} Defensive patterns
Strategy: validation
Validate before calling
import { componentMap } from './componentMapStore';
function registerOnce(type, component, spanComponent?) {
if (componentMap.has(type)) return; // idempotent
addComponent(type, component, spanComponent);
} Type guard
const isRegistered = (type: JVxeTypes): boolean => componentMap.has(type);
Try / catch
try {
addComponent(type, component);
} catch (e) {
// already registered (likely HMR); safe to ignore or call deleteComponent first
} Prevention
- Make registration idempotent with a has() guard.
- Register each type in exactly one module to avoid double imports.
- Move registration to app init, not per-component plugin install.
When it happens
Trigger: Calling addComponent for a type that was registered earlier in the same module load — most commonly during Vite HMR when registerThirdComp.ts or componentMap.ts is re-evaluated. Also triggered by importing the registration module twice (e.g. once eagerly and once lazily), or by a plugin that registers on every mount.
Common situations: Vite dev server HMR after editing a cell component or the registration file; dual registration from both registerThirdComp.ts and an @jeecg/online package; a Vue plugin that re-runs install on every component usage.
Related errors
- 【addComponent】不能使用"${type}"作为组件的name,因为这是关键字。
- [JVxeTable] ${$type} 组件尚未注册,获取增强失败
- useDescription() can only be used inside setup() or function
- useTableContext must be used after createTableContext
- sys.api.apiRequestFailed
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/a2a36411222a6c79.
Report an issue: GitHub.