Vant 从 v2 升级到 v3 完全迁移指南:v-model、组件命名与 API 变更全解析
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
Vant 3 是基于 Vue 3 重写的移动端组件库,从 Vant 2 升级并非简单的版本号替换,而是涉及组件命名、v-model 用法、全局方法注册方式等多方面的破坏性变更。本文以 Vant 官方迁移文档为骨架,结合 vant 组件库源码 逐一剖析每一项不兼容更新背后的设计原因与迁移写法,帮助你在升级 Vue 3 的同时,以最小代价完成 Vant 2 到 Vant 3 的平滑过渡。
升级前的准备:两步走策略
官方迁移文档给出的升级路径非常清晰,共两步:
- 升级 Vue 3:Vant 3 基于 Vue 3 开发,使用前必须将项目中的 Vue 升级到 3.0 以上版本,这是所有后续工作的前提。
- 处理不兼容更新:Vant 2 到 Vant 3 存在一批破坏性变更,需要对照下文逐一处理。
迁移的难点集中在第二步。Vant 3 全面拥抱 Vue 3 的新特性(如Teleport、多参数v-model、组合式 API),因此几乎所有变更都源于对 Vue 3 新语法范式的适配。下文按「组件结构 → v-model 体系 → 属性命名 → 逐组件 API → 全局方法」的顺序展开。
组件命名调整:GoodsAction 重命名为 ActionBar
Vant 2 中的「商品导航」组件GoodsAction在 Vant 3 中更名为ActionBar(行动栏),子组件同步从GoodsActionIcon、GoodsActionButton更名为ActionBarIcon、ActionBarButton。
<!-- Vant 2 --> <van-goods-action> <van-goods-action-icon text="图标" /> <van-goods-action-button text="按钮" /> </van-goods-action> <!-- Vant 3 --> <van-action-bar> <van-action-bar-icon text="图标" /> <van-action-bar-button text="按钮" /> </van-action-bar>从源码看,ActionBar.tsx 通过createNamespace('action-bar')声明了van-action-bar前缀,并通过useChildren(ACTION_BAR_KEY)与ActionBarIcon、ActionBarButton建立父子通信,因此三个标签必须整体同步替换,混用新旧前缀会导致子组件无法正确关联父组件状态。同时,badge徽标属性同样作用于ActionBarIcon(下文详述)。
废弃组件:SwitchCell 由 Cell + Switch 组合替代
Vant 2 的SwitchCell(开关单元格)在 Vant 3 中被移除,官方推荐直接使用Cell与Switch组合实现相同效果:
<!-- Vant 2 --> <van-switch-cell title="标题" v-model="checked" /> <!-- Vant 3 --> <van-cell center title="标题"> <template #right-icon> <van-switch v-model="checked" size="24" /> </template> </van-cell>迁移要点:
- 通过
Cell的#right-icon插槽将Switch放到单元格右侧,用center属性实现垂直居中; - 此时
Switch与Cell是两个独立组件,不再共享开关状态逻辑,v-model直接绑定在Switch上即可; - 若希望点击整行切换开关,需要自行在
Cell上绑定@click并同步翻转状态——这正是 Vant 3 组件更「原子化」的设计取向。
v-model 体系全面重构(Vue 3 语法适配)
Vue 3 将v-model升级为多参数模型(v-model:xxx)并统一了 prop/event 命名,Vant 3 的 v-model 变更可以分为三类,逐一对照迁移。
1. 弹窗型组件:v-model 重命名为 v-model:show
以下弹窗类组件的v-model全部重命名为v-model:show:
- ActionSheet
- Calendar
- Dialog
- ImagePreview
- Notify
- Popover
- Popup
- ShareSheet
<!-- Vant 2 --> <van-popup v-model="show" /> <!-- Vant 3 --> <van-popup v-model:show="show" />以 Popup.tsx 为例,组件内部通过emit('update:show', true)驱动显隐状态,这正是 Vue 3 命名规范中v-model:show对应的底层事件契约——v-model:show="show"等价于:show="show" @update:show="show = $event"。
2. 表单型组件:prop 重命名为 modelValue
以下表单型组件将v-model对应的 prop 重命名为modelValue,事件重命名为update:modelValue:
- Checkbox
- CheckboxGroup
- DatetimePicker
- DropdownItem
- Field
- Radio
- RadioGroup
- Search
- Stepper
- Switch
- Sidebar
- Uploader
<!-- Vant 2 --> <van-field :value="value" @input="onInput" /> <!-- Vant 3 --> <van-field :model-value="value" @update:model-value="onInput" />注意事件名在模板中需写作 kebab-case 的@update:model-value。Vue 3 的v-model语法糖本身就会展开为modelValue+update:modelValue,因此这些组件在模板中继续写v-model通常无需改动;需要改的是那些曾经手动绑定:value/@input的写法。
3. 其他 v-model 专项调整
部分组件拥有多个可绑定状态,在 Vant 3 中通过具名v-model:xxx显式表达:
| 组件 | Vant 2 | Vant 3 |
|---|---|---|
| Circle | v-model | v-model:currentRate |
| CouponList | v-model | v-model:code |
| List | v-model | v-model:loading |
| List | error.sync | v-model:error |
| Tabs | v-model | v-model:active |
| TreeSelect | active-id.sync | v-model:active-id |
| TreeSelect | main-active-index.sync | v-model:main-active-index |
这里体现了 Vue 3 的另一个重要变化:.sync修饰符已被移除,其功能由具名v-model完全取代。例如 Tabs 在 Tabs.tsx 中通过emit('update:active', newName)同步当前激活项,对应模板写法就是v-model:active:
<!-- Vant 3 --> <van-tabs v-model:active="active"> <van-tab title="标签 1" /> <van-tab title="标签 2" /> </van-tabs>徽标属性重命名:info 改为 badge
Vant 2 使用info属性展示图标右上角的数字徽标,Vant 3 为贴合社区命名习惯统一重命名为badge,影响以下组件:
- Tab
- Icon
- GridItem
- TreeSelect
- TabbarItem
- SidebarItem
- GoodsActionIcon(即现 ActionBarIcon)
同时,内部封装的 Info 组件也更名为 Badge(对应 badge 组件目录)。
<!-- Vant 2 --> <van-icon info="5" /> <!-- Vant 3 --> <van-icon badge="5" />使用文本v-model或:value绑定徽标数字的场景,只需全局将info替换为badge即可,徽标数值、角标样式等行为保持一致。
挂载属性重命名:get-container 改为 teleport
Vue 3 原生引入了Teleport组件,用于将子内容渲染到任意 DOM 节点。Vant 2 通过get-container属性提供类似能力,Vant 3 为与官方 API 对齐,将该属性重命名为teleport:
<!-- Vant 2 --> <template> <van-popup get-container="body" /> <van-popup :get-container="getContainer" /> </template> <script> export default { methods: { getContainer() { return document.querySelector('#container'); }, }, }; </script> <!-- Vant 3 --> <template> <van-popup teleport="body" /> <van-popup :teleport="container" /> </template> <script> export default { beforeCreate() { this.container = document.querySelector('#container'); }, }; </script>迁移时注意两点:
- 字符串选择器用法基本不变(如
teleport="body"、teleport="#app"); - 函数返回节点的方式需要调整:Vant 2 中
get-container接受函数并每次调用求值,Vant 3 直接透传 Vue 3Teleport的to属性(string | Element),因此应像示例那样在beforeCreate中把 DOM 引用存为实例属性再传入。
从 Popup.tsx 的渲染逻辑可以看到,teleport存在时组件直接包裹<Teleport to={props.teleport}>渲染遮罩层与过渡层,且当被 teleport 的弹窗在KeepAlive场景下失活时会自动关闭、重新激活时恢复打开。其他弹窗类组件(如 Toast 函数调用 中teleport: 'body'默认值)同样遵循该属性约定。
逐组件 API 调整清单
除上述全局性变更外,官方文档还列出以下组件级 API 调整,迁移时务必逐条核对:
Area / DatetimePicker / Picker
change事件回调不再传入组件实例。Vant 2 中change会附带组件实例便于调用内部方法,Vant 3 出于组合式 API 设计与类型收窄的考虑将其移除,事件回调只携带业务数据。若旧代码依赖实例方法(如picker.getValues()),请改用组件实例上的ref调用(Vant 3 通过expose暴露方法),或在confirm等事件中直接消费返回的数据。
Button
- 蓝色按钮对应的
type由info调整为primary; - 绿色按钮对应的
type由primary调整为success; native-type默认值由submit调整为button。
第三点在 Button.tsx 的源码中可直接验证:nativeType: makeStringProp<ButtonNativeType>('button')。这意味着 Vant 3 中不显式声明native-type的按钮不会触发表单提交,行为更符合组件库的常规预期;若你的表单依赖按钮默认提交,需显式加上native-type="submit"。
Checkbox
在Cell内部使用Checkbox时,现在需要手动添加@click.stop阻止事件冒泡,避免点击复选框时同时触发单元格的点击事件。
Dialog
allow-html属性默认关闭:Vant 3 出于安全考虑,默认将 message 作为纯文本渲染。源码中 Dialog.tsx 通过allowHtml && typeof content === 'string'决定使用innerHTML还是textContent;确需富文本时显式开启:allow-html="true"。before-close用法调整:不再传入done函数,改为通过返回值(或返回 Promise 的解析值)控制关闭。底层由 interceptor.ts 的callInterceptor统一实现:返回true(或 Promise resolve 为真值)执行关闭,返回false(或 resolve 为假值)取消关闭,Promise reject 走 error 分支。典型写法:
const beforeClose = (action) => new Promise((resolve) => { // 异步校验通过后 resolve(true) 允许关闭 setTimeout(() => resolve(true), 1000); });ImagePreview
移除async-close属性,改用新增的before-close属性实现异步关闭控制,语义与 Dialog 的before-close保持一致。
Picker
allow-html属性默认关闭(与 Dialog 同一安全策略);show-toolbar默认开启:源码 Picker.tsx 使用showToolbar: truthProp声明,truthProp即默认值为true的布尔属性;- 级联选择(
cascader)场景下,confirm、change事件回调参数将包含完整的选项对象,而非仅返回索引或文本,取值方式需相应调整。
Popover
trigger属性的默认值调整为click(Vant 2 为manual),未显式指定触发方式时,点击即弹出。
Stepper
async-change属性重命名为before-change,且使用方法调整:同样改为返回值/Promise 控制步进是否生效,写法与 Dialog 的before-close一致。
SwipeCell
open事件的detail参数重命名为name;on-close属性重命名为before-close,参数结构同步调整;before-close回调不再传入组件实例。
Toast
mask属性重命名为overlay,与 Vant 3 全局统一命名一致(如 Popup 的overlay、overlay-class、overlay-style体系)。函数式调用场景下,function-call.tsx 的默认配置同样使用overlay: false字段。
TreeSelect
navclick事件重命名为click-nav(点击左侧导航栏);itemclick事件重命名为click-item(点击右侧选项项)。
全局方法:必须先 app.use 注册
Vant 2 默认提供$toast、$dialog等挂载在 Vue 原型上的全局方法。Vue 3 不再支持在原型链上直接挂载,因此从 Vant 3 起,使用全局方法前必须先将对应组件通过app.use注册到当前 app 实例:
import { Toast, Dialog, Notify } from 'vant'; // 将 Toast 等组件注册到 app 上 app.use(Toast); app.use(Dialog); app.use(Notify); // app 内的子组件可以直接调用 $toast 等方法 export default { mounted() { this.$toast('提示文案'); }, };该机制在源码中体现为:Toast、Dialog、Notify等组件在install时向 app 提供全局属性(如app.config.globalProperties.$toast),并同时提供可独立导入的函数式 API(如showToast、showDialog、showNotify)。值得注意的是,函数式 API 不依赖组件注册,showToast 通过mountComponent动态挂载实例即可使用;而$toast这类实例方法必须完成注册后才能调用,且注册对象是当前 app 而非全局,即「哪个 app 注册了,哪个 app 的子树内才能用」。
迁移自检清单
完成上述变更后,建议按以下清单逐项自查,避免遗漏:
- Vue 升级到 3.0+,且引入方式、
createApp挂载流程已切换为 Vue 3 写法; - 全局搜索
van-goods-action、van-goods-action-icon、van-goods-action-button,替换为van-action-bar系列; - 全局搜索
van-switch-cell,改为Cell+Switch组合; - 弹窗类组件(ActionSheet、Calendar、Dialog、ImagePreview、Notify、Popover、Popup、ShareSheet)的
v-model改为v-model:show; - 表单类组件中手动绑定的
:value/@input改为:model-value/@update:model-value; - 处理
.sync修饰符,统一改写为具名v-model:xxx; - 将
info徽标属性全局替换为badge; - 将
get-container全局替换为teleport,并调整函数式传参写法; - 按上文 API 调整清单核对 Button、Dialog、Picker、Stepper、SwipeCell、Toast、TreeSelect 等组件的属性与事件;
- 确认
$toast、$dialog、$notify等全局方法已通过app.use注册,或改用函数式 API(showToast等)。
迁移完成后建议重点回归验证三类场景:弹窗的显隐控制与异步关闭、表单的双向绑定、Picker 等选择器的回调参数取值。Vant 3 的其余新特性(如主题定制、按需引入方式)不受本次迁移影响,可参照仓库内各组件的 README.zh-CN.md 逐步探索。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考