搜索 K
主题
@vaultkit/vitepress-plugin-donate 是文章尾部赞赏卡 Vue 组件:支付宝 / 微信收款码页签切换 + PayPal 外链,三渠道按配置门控——配了什么就显示什么,全缺时整卡消失,宿主连 v-if 都不用包。交互是居中的「赞赏」按钮点击展开面板(非模态),SSR 首帧收起态一致、无水合差异。
零依赖:无弹窗库无页签库,颜色走 --vp-c-* 变量明暗自适应,文案经 labels 全量注入,prefers-reduced-motion 下关闭展开过渡。本文覆盖全部导出、channels 三渠道与 labels 四字段的逐项详解,以及按配置门控的设计逻辑。
独立博客的变现动线里,「赞赏」是最轻的一种:不挂广告、不设付费墙,读者觉得文章有价值就扫码请作者喝杯咖啡。但自己写赞赏组件有几个繁琐点:
本包把这些都处理好了,peer 只有 vue ^3.5.0。
pnpm add -D @vaultkit/vitepress-plugin-donate使用 @vaultkit/vitepress-theme-vault 主题则开箱即带(主题内 NoteDonate 适配层从站点配置读渠道,配置门控——不配渠道则整个功能不出现)。
推荐挂在 Layout 的 #doc-after 插槽,正文后、版权卡前(先转化、后声明):
<script setup lang="ts">
import { DonateCard } from '@vaultkit/vitepress-plugin-donate'
import DefaultTheme from 'vitepress/theme'
</script>
<template>
<DefaultTheme.Layout>
<template #doc-after>
<DonateCard
:channels="{
alipay: { qrcode: '/donate-alipay.png', label: '支付宝' },
wechat: { qrcode: '/donate-wechat.png', label: '微信' },
paypal: { url: 'https://paypal.me/yourname' },
}"
:labels="{
button: '赞赏支持',
hint: '如果这篇文章帮到了你,可以请作者喝杯咖啡 ☕',
qrAlt: name => `${name}收款码`,
aria: '赞赏支持',
}"
/>
</template>
</DefaultTheme.Layout>
</template>收款码图片放进 public/ 目录即可按绝对路径引用。
本包发布的是含 .vue 源文件的产物,消费方 VitePress 项目必须把它加进 vite.ssr.noExternal,否则 SSR 构建会报错:
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
export default defineConfig({
vite: { ssr: { noExternal: ['@vaultkit/vitepress-plugin-donate'] } },
})| 导出 | 形态 | 说明 |
|---|---|---|
DonateCard | Vue 组件(具名 + 默认导出) | 赞赏卡本体 |
DonateChannels | 类型 | 三渠道配置形状 |
DonateLabels | 类型 | 四字段文案注入 |
只有两个 props:channels(必填)与 labels(可选)。字段不多,但每个都有门控语义,值得逐个说清。
DonateChannels,必填| 字段 | 类型 | 形态 | 缺省显示名 |
|---|---|---|---|
alipay | { qrcode: string, label?: string } | 二维码页签 | Alipay |
wechat | { qrcode: string, label?: string } | 二维码页签 | WeChat Pay |
paypal | { url: string, label?: string } | 外链页签(新窗口直开) | PayPal |
alipay / wechat:qrcode 是收款码图片地址(public/ 下的绝对路径或完整 URL),label 覆盖页签显示名——中文站点务必传 '支付宝' / '微信',否则页签显示英文缺省名。显示名同时用于页签文字、二维码下方 figcaption 与 qrAlt 函数的入参。
paypal:url 是 paypal.me/... 收款链接。它不是二维码——页签形态为外链,点击直接新窗口打开(target="_blank" rel="noopener"),不切换面板内容,页签上带外链角标图标提示这个差异。
三种典型组合:
// 只面向国内读者:双码
const channels = {
alipay: { qrcode: '/alipay.png', label: '支付宝' },
wechat: { qrcode: '/wechat.png', label: '微信' },
}
// 只面向海外读者:仅 PayPal(渲染为独立外链按钮,无页签条)
const channels2 = {
paypal: { url: 'https://paypal.me/yourname', label: '请我喝咖啡' },
}
// 全渠道
const channels3 = {
alipay: { qrcode: '/alipay.png', label: '支付宝' },
wechat: { qrcode: '/wechat.png', label: '微信' },
paypal: { url: 'https://paypal.me/yourname' },
}DonateLabels,默认 {}| 字段 | 类型 | 默认值 | 位置与作用 |
|---|---|---|---|
button | string | 'Sponsor' | 居中的赞赏按钮文案(前置 lucide 爱心图标) |
hint | string | 无(缺省不渲染) | 展开面板顶部的提示语整句 |
qrAlt | (channel: string) => string | `${name} QR code` | 二维码图片的 alt 文案,入参是渠道显示名 |
aria | string | 'Sponsor' | 卡片容器的 aria-label |
中文文案完整示例:
const labels = {
button: '赞赏支持',
hint: '如果这篇文章帮到了你,可以请作者喝杯咖啡 ☕',
qrAlt: (name: string) => `${name}收款码`,
aria: '赞赏支持',
}两个细节:hint 与版权卡的 notice 同款语义——缺省整行不渲染,而非回退英文;qrAlt 是函数注入(与 reading-time 的 label 同理),因为 alt 文案里要嵌渠道名,传入 label 后生成的就是「支付宝收款码」这样读屏可懂的描述。
组件内部把 channels 归一化成两个 computed:
const qrTabs = computed(() =>
(['alipay', 'wechat'] as const).flatMap((key) => {
const channel = props.channels[key]
return channel?.qrcode
? [{ key, name: channel.label ?? QR_DEFAULTS[key], qrcode: channel.qrcode }]
: []
}),
)
const hasChannels = computed(() => qrTabs.value.length > 0 || paypal.value !== null)由此推导出全部渲染分支,宿主不用写任何门控代码:
hasChannels 为假,整卡消失(连赞赏按钮都没有)activeTab 回退 qrTabs[0])页签条的显示条件是 qrTabs.length > 1 || (qrTabs.length && paypal)——恰好覆盖「需要选择」的所有情形,不多渲染一个像素。
面板不是弹窗(无遮罩、不锁滚动、不抢焦点),就是按钮下方原位展开的一块 max-width: 320px 的软底卡片——移动端不用处理弹层滚动穿透,无障碍上也免去焦点陷阱的复杂度。open 状态初始恒为 false,SSR 输出与客户端首帧一致(都是收起态),无水合差异。按钮带 aria-expanded 语义,读屏用户能感知展开状态。
<img> 固定 width/height 180 且 loading="lazy"——尺寸预留防布局位移,懒加载不抢正文带宽;白底圆角处理,暗色模式下扫码对比度不受影响prefers-reduced-motion: reduce 下连同按钮 hover 位移一并关闭--vp-c-brand-* / --vp-c-bg-soft 等 VitePress 语义变量,明暗自适应零配置#doc-after 的顺序是赞赏 → 版权 → 评论——转化入口放最前(读者读完情绪最热时),声明与讨论其后channels 是响应式 prop,可按 frontmatter 对特定文章关闭赞赏(传空对象整卡消失)active 状态是组件局部 ref,路由切换后重置为首个渠道——刻意不持久化,赞赏面板的状态没有跨页记忆的价值Q:整个卡片不显示?channels 全空,或二维码渠道漏传 qrcode、PayPal 漏传 url——字段缺失即视为未配置,这是门控的设计行为。
Q:页签显示英文 Alipay / WeChat Pay? 没传 label。渠道显示名缺省英文,中文站点在每个渠道里传 label: '支付宝' / label: '微信'。
Q:点 PayPal 页签怎么面板没切换? 按设计:PayPal 是外链形态,直接新窗口打开收款页,页签上的外链角标就是这个提示。
Q:SSR 构建报 .vue 无法解析? 没配 vite.ssr.noExternal,见上文安装一节。