Vant 4 ShareSheet 分享面板组件完全指南:从用法、API 到源码原理
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
ShareSheet 是 Vant 4 中一个底部弹起的分享面板组件,用于展示各分享渠道对应的操作按钮(微信、微博、复制链接、二维码等),组件本身不包含任何具体的分享逻辑。在移动端 H5 业务中,它是承接"分享给好友 / 分享海报 / 生成小程序码"等入口的标准 UI 方案。读完本文,你将掌握 ShareSheet 的完整配置(options数据结构、17 个 Props、7 个事件、3 个插槽、14 个主题变量),并了解其基于 Popup 组件的底层实现原理与测试验证方式。
组件定位与设计思想
ShareSheet 的设计理念很明确:只负责"展示"分享面板,不负责"执行"分享动作。正如 README 开头所述,它是一个 "pop-up sharing panel at the bottom for displaying the action buttons corresponding to each sharing channel, without specific sharing logic"。
这是因为移动端分享场景极度碎片化:
- 微信内没有公开的分享 API,只能引导用户点击右上角菜单;
- 各 App 内需要通过 JSBridge 调用原生 SDK;
- 海报、二维码类分享则往往需要配合弹层组件展示图片。
因此 Vant 将"面板 UI"与"分享逻辑"解耦,开发者只需提供options数组定义分享渠道,并在select事件中自行对接业务分享能力。从源码结构看,ShareSheet 目录下也只有 ShareSheet.tsx(组件实现)、index.less(样式)、types.ts(主题变量类型)与 index.ts(导出),没有依赖任何分享 SDK。
快速引入
ShareSheet 支持按需引入与全局注册。通过app.use全局注册:
import { createApp } from 'vue'; import { ShareSheet } from 'vant'; const app = createApp(); app.use(ShareSheet);从 index.ts 可以看到,组件通过withInstall包装后同时支持默认导出与命名导出,并注册了全局组件名VanShareSheet。更多组件注册方式(如 Vite/RSC 按需自动引入)可参考文档 组件注册。
基础用法
ShareSheet 通过options属性定义分享选项,数组的每一项是一个对象,对象格式见下文"Option 数据结构"一节。面板显隐由v-model:show双向绑定控制,点击某个选项触发select事件:
<van-cell title="显示分享面板" @click="showShare = true" /> <van-share-sheet v-model:show="showShare" title="立即分享给好友" :options="options" @select="onSelect" />import { ref } from 'vue'; import { showToast } from 'vant'; export default { setup() { const showShare = ref(false); const options = [ { name: '微信', icon: 'wechat' }, { name: '微博', icon: 'weibo' }, { name: '复制链接', icon: 'link' }, { name: '分享海报', icon: 'poster' }, { name: '二维码', icon: 'qrcode' }, ]; const onSelect = (option) => { showToast(option.name); showShare.value = false; }; return { options, onSelect, showShare, }; }, };select事件的回调参数为(option: Option, index: number),即被点击的选项对象及其在数组中的索引。在 test/index.spec.ts 的测试用例中,可以验证点击选项后组件确实以[{ icon: 'wechat', name: 'wechat' }, 0]的形式派发select事件。
展示多行选项
当分享选项较多时,可以把options定义为数组嵌套的格式,每个子数组会作为一行选项展示,行与行之间自动绘制顶部细分割线:
<van-share-sheet v-model:show="showShare" title="立即分享给好友" :options="options" />import { ref } from 'vue'; export default { setup() { const showShare = ref(false); const options = [ [ { name: '微信', icon: 'wechat' }, { name: '朋友圈', icon: 'wechat-moments' }, { name: '微博', icon: 'weibo' }, { name: 'QQ', icon: 'qq' }, ], [ { name: '复制链接', icon: 'link' }, { name: '分享海报', icon: 'poster' }, { name: '二维码', icon: 'qrcode' }, { name: '小程序码', icon: 'weapp-qrcode' }, ], ]; return { options, showShare, }; }, };从 ShareSheet.tsx 的实现可以看到,renderRows通过Array.isArray(options[0])判断是否为多行结构,多行时逐行调用renderOptions,且除第一行外其余行都会加上--border修饰类,对应 index.less 中基于hairline混合宏绘制的 1px 顶部边框,视觉上区分每组分享渠道。
自定义图标
除了内置的 8 种分享图标外,可以直接在icon字段中传入图片 URL来使用任意自定义图标:
<van-share-sheet v-model:show="showShare" :options="options" />import { ref } from 'vue'; export default { setup() { const showShare = ref(false); const options = [ { name: '名称', icon: 'https://fastly.jsdelivr.net/npm/@vant/assets/custom-icon-fire.png', }, { name: '名称', icon: 'https://fastly.jsdelivr.net/npm/@vant/assets/custom-icon-light.png', }, { name: '名称', icon: 'https://fastly.jsdelivr.net/npm/@vant/assets/custom-icon-water.png', }, ]; return { options, showShare, }; }, };判断逻辑在 ShareSheet.tsx 的isImage函数中:只要icon字符串包含/字符就按图片 URL 处理,渲染为<img>标签(class 为van-share-sheet__image-icon);否则按内置图标名处理,通过iconMap映射后交给<van-icon>渲染。这个"斜杠即图片"的约定非常轻量,也意味着只要 URL 合法(含路径分隔符)即可生效。仓库自带 Demo demo/index.vue 中除 CDN 图片外,还混合使用了内置图标label,验证了两种模式可以并存。
展示描述信息
通过description属性可以设置标题下方的整体描述文字;在某个options项内部设置description字段,则可为该分享选项单独添加一行描述:
<van-share-sheet v-model:show="showShare" :options="options" title="立即分享给好友" description="描述信息" />import { ref } from 'vue'; export default { setup() { const showShare = ref(false); const options = [ { name: '微信', icon: 'wechat' }, { name: '微博', icon: 'weibo' }, { name: '复制链接', icon: 'link', description: '描述信息' }, { name: '分享海报', icon: 'poster' }, { name: '二维码', icon: 'qrcode' }, ]; return { options, showShare, }; }, };渲染细节:标题与描述共用.van-share-sheet__header头部区域,只有title或description非空时才渲染该区域(见 ShareSheet.tsx);选项级描述渲染在选项名下方,样式类为van-share-sheet__option-description。测试 test/index.spec.ts 也覆盖了 description 渲染与清空后的隐藏行为。
Props 完整说明
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| v-model:show | 是否显示分享面板 | boolean | false |
| options | 分享选项 | Option[] | [] |
| title | 顶部标题 | string | - |
| cancel-text | 取消按钮文字,传入空字符串可以隐藏按钮 | string | '取消' |
| description | 标题下方的辅助描述文字 | string | - |
| duration | 动画时长,单位秒,设置为 0 可以禁用动画 | number | string | 0.3 |
| z-index | 将面板的 z-index 层级设置为一个固定值 | number | string | 2000+ |
| round | 是否显示圆角 | boolean | true |
| overlay | 是否显示遮罩层 | boolean | true |
| overlay-class | 自定义遮罩层类名 | string | Array | object | - |
| overlay-style | 自定义遮罩层样式 | object | - |
| lock-scroll | 是否锁定背景滚动 | boolean | true |
| lazy-render | 是否在显示弹层时才渲染内容 | boolean | true |
| close-on-popstate | 是否在页面回退时自动关闭 | boolean | true |
| close-on-click-overlay | 是否在点击遮罩层后关闭 | boolean | true |
| safe-area-inset-bottom | 是否开启底部安全区适配 | boolean | true |
| teleport | 指定挂载的节点,等同于 Teleport 组件的 to 属性 | string | Element | - |
| before-close | 关闭前的回调函数,返回false可阻止关闭,支持返回 Promise | (action: string) => boolean | Promise<boolean> | - |
Props 的底层来源
从源码角度看,这 17 个 Props 中大部分并非 ShareSheet 自行定义,而是继承自 Popup 弹层组件。在 ShareSheet.tsx 中,shareSheetProps通过extend({}, popupSharedProps, {...})合并了 popup/shared.ts 中声明的共享属性(show、zIndex、overlay、duration、teleport、lockScroll、lazyRender、beforeClose、overlayStyle、overlayClass、closeOnClickOverlay等),再叠加round、closeOnPopstate、safeAreaInsetBottom三个truthProp(默认为 true 的布尔属性)以及title、options、cancelText、description四个自有属性。
options使用makeArrayProp工厂函数生成,默认值为[],其类型为ShareSheetOption[] | ShareSheetOption[][],即支持单行与多行两种形态(见 ShareSheet.tsx 的ShareSheetOptions类型)。最终渲染时,整个面板结构被包裹在position="bottom"的<Popup>内,因此 ShareSheet 天然获得 Popup 的动画、遮罩、锁定滚动、Teleport 等全部能力——这也是其 Props 如此丰富的原因。
Option 数据结构
options属性为一个对象数组,数组中的每个对象配置一个分享选项,可包含以下字段:
| 键名 | 说明 | 类型 |
|---|---|---|
| name | 分享渠道名称 | string |
| description | 分享选项描述 | string |
| icon | 图标,可选值为wechatweiboqqlinkqrcodeposterweapp-qrcodewechat-moments,支持传入图片 URL | string |
| className | 分享选项类名,会设置到分享项根元素上 | string |
对应的 TypeScript 定义在 ShareSheet.tsx:
export type ShareSheetOption = { name: string; icon: string; className?: string; description?: string; };其中className字段用于给单个选项追加自定义类名。测试 test/index.spec.ts 验证了className: 'foo'会被正确挂到.van-share-sheet__option元素上,可用于对特定渠道做差异化样式。
内置图标与颜色映射
icon的 8 个内置值在 ShareSheet.tsx 的iconMap中映射为对应的 Vant 图标名:
| 分享 icon 值 | 实际渲染的图标 |
|---|---|
| wechat-moments | wechat-moments |
| link | link-o |
| qrcode | qr |
| poster | photo-o |
| weapp-qrcode | miniprogram-o |
同时 index.less 为品牌渠道预设了圆形底与品牌色:微信绿色(#0bc15f)、微博红色(#ee575e)、QQ 蓝色(#38b9fa)、朋友圈绿色(#7bc845),未映射的图标名会直接透传给<van-icon>渲染。
Events 事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| select | 点击分享选项时触发 | option: Option, index: number |
| cancel | 点击取消按钮时触发 | - |
| open | 打开面板时触发 | - |
| close | 关闭面板时触发 | - |
| opened | 打开面板且动画结束后触发 | - |
| closed | 关闭面板且动画结束后触发 | - |
| click-overlay | 点击遮罩层时触发 | event: MouseEvent |
其中select、cancel由 ShareSheet 自身声明(见 ShareSheet.tsx 的emits: ['cancel', 'select', 'update:show']),其余open/close/opened/closed/click-overlay事件由内部 Popup 透传而来。取消按钮的点击行为是先派发update:show(false)关闭面板,再派发cancel事件(ShareSheet.tsx),测试用例 test/index.spec.ts 对这一顺序有明确断言。
Slots 插槽
| 名称 | 说明 |
|---|---|
| title | 自定义顶部标题 |
| description | 自定义描述文字 |
| cancel | 自定义取消按钮内容 |
插槽优先级高于对应属性:传入title插槽时,插槽内容会覆盖title属性(见 ShareSheet.tsx 与 #L140-L149 的 cancel 插槽逻辑)。特别地,cancel插槽与cancel-text属性二选一渲染取消按钮;当两者都为空时,取消按钮整体不渲染——测试 test/index.spec.ts 验证了cancelText: ''时按钮会被隐藏。
类型定义
组件从vant包中导出以下类型,供 TypeScript 项目使用:
import type { ShareSheetProps, ShareSheetOption, ShareSheetOptions, } from 'vant';此外还可导入ShareSheetThemeVars主题变量类型,见 types.ts,它声明了 14 个shareSheetXxx可选字段,与下方 CSS 变量一一对应,便于在ConfigProvider主题定制时获得类型提示。
主题定制:CSS 变量
ShareSheet 提供了以下 CSS 变量,可直接在根节点覆盖,或通过 ConfigProvider 组件 统一注入主题:
| 名称 | 默认值 | 描述 |
|---|---|---|
| --van-share-sheet-header-padding | var(--van-padding-sm) var(--van-padding-md) var(--van-padding-base) | 头部内边距 |
| --van-share-sheet-title-color | var(--van-text-color) | 标题颜色 |
| --van-share-sheet-title-font-size | var(--van-font-size-md) | 标题字号 |
| --van-share-sheet-title-line-height | var(--van-line-height-md) | 标题行高 |
| --van-share-sheet-description-color | var(--van-text-color-2) | 描述文字颜色 |
| --van-share-sheet-description-font-size | var(--van-font-size-sm) | 描述文字字号 |
| --van-share-sheet-description-line-height | 16px | 描述文字行高 |
| --van-share-sheet-icon-size | 48px | 图标尺寸 |
| --van-share-sheet-option-name-color | var(--van-gray-7) | 选项名称颜色 |
| --van-share-sheet-option-name-font-size | var(--van-font-size-sm) | 选项名称字号 |
| --van-share-sheet-option-description-color | var(--van-text-color-3) | 选项描述颜色 |
| --van-share-sheet-option-description-font-size | var(--van-font-size-sm) | 选项描述字号 |
| --van-share-sheet-cancel-button-font-size | var(--van-font-size-lg) | 取消按钮字号 |
| --van-share-sheet-cancel-button-height | 48px | 取消按钮高度 |
| --van-share-sheet-cancel-button-background | var(--van-background-2) | 取消按钮背景色 |
这些变量的默认值全部定义在 index.less 的:root, :host选择器中,绝大多数引用 Vant 设计令牌(如--van-padding-md、--van-font-size-sm、--van-text-color),修改全局设计变量即可联动生效。例如把图标放大:
:root { --van-share-sheet-icon-size: 56px; }常见问题:如何实现分享逻辑
ShareSheet 刻意不内置分享逻辑。在不同 App 或浏览器中,分享接口与方式差异很大,需要开发者根据业务场景自行在select事件回调中对接:
微信内分享
微信未提供公开的分享 API,通常的做法是引导用户点击右上角菜单进行分享,分享面板仅作为引导入口。
App 内分享
在 App 内可以通过 JSBridge 调用原生应用的 SDK 完成分享,ShareSheet 的select回调中拿到option后,按渠道名分发到对应的桥接方法即可。
分享海报或二维码
海报、二维码类"分享"本质上不是分享 API 调用,而是内容展示。可以配合 Popup 组件 以弹层形式展示图片,再引导用户长按保存图片进行分享。
源码级要点小结
- 基于 Popup 的复合组件:ShareSheet = Popup(
position="bottom")+ 头部 + 选项网格 + 取消按钮,共享属性通过popupSharedProps合并(popup/shared.ts),这也是它拥有遮罩、动画、锁滚动、Teleport 等能力的原因。 - 图标双模式:
icon含/渲染<img>,否则走iconMap映射到 Vant 图标(ShareSheet.tsx)。 - 多行渲染:
Array.isArray(options[0])判定多行结构,首行外自动加细分隔线(ShareSheet.tsx)。 - 取消按钮可隐藏:
cancel-text传空字符串即不渲染取消按钮,且有对应测试用例保障(test/index.spec.ts)。 - 可访问性细节:每个分享选项渲染为
role="button"+tabindex={0},并附带HAPTICS_FEEDBACK触摸反馈类,兼顾键盘操作与移动端按压反馈。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考