8.2 KiB
8.2 KiB
Naive UI → Arco Design Vue 组件映射表
Phase 005 起,新代码与迁移代码一律使用 Arco Design Vue。本文表是迁移的唯一映射依据: 先查表,再改代码。表中没有的组件,先在此表补一行再动手。
- 全局注册:
main.ts全量app.use(ArcoVue),不做按需引入 - 主题:
body[arco-theme='dark'](见styles/arco-theme.ts与docs/ui-baseline.md) - hooks:
useMessage/useDialog/useNotification适配层在packages/client/src/composables/, 签名冻结,不得在页面里直接 import Arco 的Message/Modal/Notification
Provider / hooks
| Naive | Arco | 备注 |
|---|---|---|
NConfigProvider |
直接用 body[arco-theme] |
不需要组件 |
NMessageProvider / useMessage |
composables/useMessage.ts |
适配层,签名冻结 |
NDialogProvider / useDialog |
composables/useDialog.ts |
positiveText→okText、onPositiveClick→onBeforeOk(resolve false / reject → 保持打开)由适配层转换 |
NNotificationProvider / useNotification |
composables/useNotification.ts |
description→content 由适配层转换 |
基础组件(按当前仓库用量排序)
| Naive | Arco | 用量 | 备注 |
|---|---|---|---|
NButton |
a-button |
732 | block→long、size="tiny"→size="mini"、type="warning"→type="primary" status="warning"、quaternary/secondary→type="secondary"(Arco status 只有 normal|success|warning|danger,没有 secondary) |
NInput |
a-input / a-textarea |
160 | type="textarea" 用独立 a-textarea;a-input 关闭了 attrs 透传,autofocus 无效,需 ref + focus() |
NModal |
a-modal |
138 | v-model:show→v-model:visible;preset="dialog" 无对应,信息型弹窗用 :footer="false"(footer 为 Boolean prop,默认 true) |
NFormItem |
a-form-item |
136 | 校验规则 API 不同(rules 结构、required 表达式)需适配 |
NSelect |
a-select |
97 | options 的 label/value 语义一致;consistent-menu-width 等 prop 无对应 |
NSpin |
a-spin |
86 | show→loading |
NTooltip |
a-tooltip |
76 | trigger="hover" 默认一致;placement→position;触发元素走默认插槽,naive 的 <template #trigger> 必须拆掉 |
NPopconfirm / NTooltip 的 #trigger 槽 |
Arco 默认插槽 | — | Arco 的 a-tooltip / a-popconfirm / a-dropdown 都用默认插槽承载触发元素,naive 的 <template #trigger> 写法会静默丢内容 |
| Arco Trigger 嵌套 | 不可嵌套,无论谁包谁 | — | a-tooltip / a-dropdown / a-popconfirm 内部都用 mergeFirstChild 找首个元素节点并各自接管事件;嵌套时两个 popup 容器互相踩,菜单点不开(.arco-trigger-popup-wrapper 卡在过渡中间态、选项数为 0)。同一触发元素要同时有说明文案和下拉时,说明文案改用原生 :title,不要套 a-tooltip |
NTag |
a-tag |
57 | bordered prop 存在(默认 true);size="small" 一致;naive type="info"→color="arcoblue" 等内建色(red/orange/green/arcoblue/...),自定义色用 color 传 CSS 颜色 |
NSwitch |
a-switch |
53 | value/v-model 一致 |
NSpace |
a-space |
51 | size 语义一致(数字/预设名) |
NInputNumber |
a-input-number |
43 | min/max/precision 一致 |
NPopconfirm |
a-popconfirm |
36 | positive-text→ok-text;@positive-click→@ok |
NForm |
a-form |
32 | 校验规则 API 不同,需适配 |
NTabPane |
a-tab-pane |
30 | NTabs/NTabPane → a-tabs/a-tab-pane |
NDrawer / NDrawerContent |
a-drawer |
27 | Arco 无独立 content 组件;footer slot 用 a-drawer 的 footer |
NDropdown |
a-dropdown |
25 | Arco 没有 droplist prop:菜单内容必须写进 #content 插槽、用 a-doption / a-dsubmenu 声明。传 :droplist 会被静默忽略,弹层能打开但 <ul class="arco-dropdown-list"> 是空的;子菜单用 a-dsubmenu + 自己的 #content 插槽 |
NDescriptions / NDescriptionsItem |
a-descriptions / a-descriptions-item |
19 | 基本一致 |
NAlert |
a-alert |
19 | type 一致;show-icon 一致 |
NRadioButton / NRadioGroup / NRadio |
a-radio(type="radio-button")/ a-radio-group |
32 | Arco radio-button 是 a-radio 的 type prop |
NCheckbox / NCheckboxGroup |
a-checkbox / a-checkbox-group |
17 | options 数组语义一致 |
NEmpty |
a-empty |
12 | 基本一致 |
NCollapse / NCollapseItem |
a-collapse / a-collapse-item |
24 | arrow 语义不同 |
NUpload |
a-upload |
9 | file-list 结构不同,需适配 |
NText |
<a-typography> / 原生 span |
8 | 多数场景直接用原生元素 + class |
NTabs |
a-tabs |
6 | 见 NTabPane 行 |
NBreadcrumb / NBreadcrumbItem |
a-breadcrumb / a-breadcrumb-item |
8 | 基本一致 |
NDataTable |
a-table |
4 | 列定义 API 差异最大(columns/render/slots),逐例处理 |
NSlider |
a-slider |
3 | 基本一致 |
NScrollbar |
a-scrollbar |
3 | Arco 全局可用,无需组件包裹注册 |
NPopselect |
a-dropdown |
3 | 无直接对应。实测 a-dropdown + :droplist(key/content)可完整替代 |
NPopover |
a-popover |
3 | trigger 语义有差异 |
NIcon |
<icon-*>(@arco-design/web-vue/es/icon) |
3 | Arco 图标是独立组件,需 import |
NBadge |
a-badge |
3 | value→count |
NTree |
a-tree |
2 | 结构差异大,逐例处理 |
NCard |
a-card |
— | 当前仓库未直接使用;若出现按此映射 |
NProgress |
a-progress |
— | 当前仓库未直接使用;若出现按此映射 |
NLayout / NLayoutSider |
a-layout / a-layout-sider |
— | 当前仓库未使用(侧栏是手写 div),保持手写或按需引入 |
迁移批次(Phase 006/007 使用)
- Phase 006:聊天页(
components/hermes/chat/)——消息气泡内部不用 Arco 组件(性能红线) - Phase 007:其余视图 +
stores/+composables/批量替换 + 删除 naive-ui 依赖
例外
- 聊天虚拟滚动区不用
a-scrollbar包裹。VirtualMessageList.vue直接用vue-virtual-scroller的DynamicScroller独占滚动(原本也没有NScrollbar), 额外包一层滚动容器会破坏高度测量与 sticky 定位。 - 测试里 Arco 组件的 stub 必须用 Arco 内部组件名(
Dropdown/Modal/Spin), 而不是模板里的a-*标签名:app.use(ArcoVue)之后组件已全局注册,VTU 的stubs只按内部名匹配,用ADropdown会静默不生效。 ChatInput.vue的推理强度按钮、输入设置按钮放弃a-tooltip,改用原生:title。 这两个按钮必须挂a-dropdown,而 Trigger 不能嵌套(见上表同名行),因此失去 Arco 的悬浮气泡;文案与aria-label都保留,信息量不变。- Arco(和 Vue 本身)不会对拼错的 prop 报错,未声明的 prop 会作为普通 attribute
落到根元素上。所以「弹层能打开、但内容是空的」几乎总是 prop 写错而不是数据为空。
排查手法:
el.__vueParentComponent.props看目标组件到底认不认这个 prop, 再看el.outerHTML确认渲染出来的骨架。回归由tests/e2e/chat-input-dropdown.spec.ts卡住。 - 迁移时最容易漏的是 CSS 里的 naive 类名,因为
grep naive-ui匹配不到:deep(.n-button__content)这种写法。聊天页原本有 35 处这类死样式,在 Arco 下 全部不生效,必须逐条改写成 Arco 类名。注意三处结构差异:a-button没有 content 包裹层(naive 有.n-button__content),gap/min-width要直接写在 button 上a-modal没有body-wrapper分层,用.arco-modal/.arco-modal-bodya-drawer同理,没有content/body-content-wrapper分层
useKeyboard.ts里查.n-modal-mask的 Esc 兜底属于跨组件的全局约定,迁移 组件时最容易漏掉。改弹窗库时必须同步 grep.n-modal|.n-drawer|.n-base-这类 字符串字面量里的类名,而不只是组件 import。回归由tests/client/session-search.test.ts的 Esc 用例卡住。