首页
/ Axios 文档站 Sponsors 赞助商页面:数据管道与 Vue 渲染实现全解析

Axios 文档站 Sponsors 赞助商页面:数据管道与 Vue 渲染实现全解析

2026-09-04 22:12:50作者:舒璇辛Bertina

本文以 axios 仓库中的文档站赞助商页面 sponsors.md 为核心,解析这一 VitePress + Vue 单文件组件页面的完整实现:从 sponsors.json 数据的获取与清洗脚本,到页面的分层渲染、响应式网格与分级标签样式,并给出可直接运行的数据更新与文档构建流程,帮助你理解一个“数据驱动型文档页面”从 API 拉取到前端展示的全链路。

1. 赞助商页面在文档站中的定位

axios 的文档站基于 VitePress 构建(docs/package.json 中声明了 vitepress ^1.6.4vue ^3.5.32 依赖)。赞助商页面并不是普通 Markdown 页面,而是一个带 <script setup><style module> 的 Vue 单文件组件式页面,且同时提供四种语言版本:

四个版本在 docs/.vitepress/config.mts 的导航中分别注册为 /pages/misc/sponsors/es/pages/misc/sponsors/fr/pages/misc/sponsors/zh/pages/misc/sponsors,即每个语言版本都有独立的顶级导航入口 “Sponsors / 赞助商 / Patrocinadores”。

以西班牙语版页面 docs/es/pages/misc/sponsors.md 为例,其正文只有一句话:“Axios cuenta con el apoyo de las siguientes organizations……”,并指向 Open Collective 页面作为赞助入口说明;其余部分全部是数据驱动的组件代码。值得注意的是,四个版本页面的 frontmatter 均声明了 search: false,意味着该页面不参与文档全文搜索索引——因为页面内容是动态渲染的 Logo 墙,没有可检索的静态文本。

2. 页面组件实现:分层数据与响应式渲染

2.1 数据读取与层级拍平

页面 <script setup> 部分(docs/es/pages/misc/sponsors.md):

<script setup>
import allSponsors from '../../../data/sponsors.json';

const sponsors = [...(allSponsors.platinum ?? []), ...(allSponsors.gold ?? []), ...(allSponsors.silver ?? []), ...(allSponsors.bronze ?? []), ...(allSponsors.backer ?? [])];

const capitalizeFirstLetter = (word) => {
  return String(word).charAt(0).toUpperCase() + String(word).slice(1);
};
</script>

三个实现要点:

  1. 相对导入路径。该文档位于 docs/es/pages/misc/ 目录下,../../../data/sponsors.json 实际解析到仓库内的 docs/data/sponsors.json(英文版则写作 ../../data/sponsors.json)。也就是说,四种语言版本共享同一份赞助商数据源,差异仅在文案与导航。
  2. ?? [] 空值合并sponsors.json 中只包含实际存在的层级键,缺失的层级(例如某期没有 platinum 赞助商)会得到空数组而非 undefined,保证展开运算符安全。
  3. 固定展示顺序。层级按 platinum → gold → silver → bronze → backer 的顺序拍平成一维数组,页面上赞助商卡片的排列顺序即由该数组顺序决定,等级越高越靠前。

2.2 卡片渲染逻辑

模板部分(docs/es/pages/misc/sponsors.md)用 v-for 遍历拍平后的 sponsors 数组,每张卡片包含三部分:

<img :src="sponsor.imageUrl" :alt="sponsor.name" style="max-height: 72px; width: 100%; object-fit: contain;" />
<span :class="$style[`tagSponsor${capitalizeFirstLetter(sponsor.tier)}`]">{{ capitalizeFirstLetter(sponsor.tier) }}</span>
<a :href="sponsor.website" rel="noopener noreferrer" target="_blank" :class="$style.sponsorName">{{ sponsor.name }}</a>
  • Logo 图片max-height: 72px + object-fit: contain 保证不同比例的 Logo 都能等比缩放、不变形;
  • 动态 class 绑定tagSponsor${capitalizeFirstLetter(sponsor.tier)} 用计算属性动态拼出 tagSponsorPlatinumtagSponsorGold 等样式类名,将数据中的小写 tier 值与 CSS 模块类名关联起来;
  • 外链安全:赞助者网站链接统一加了 rel="noopener noreferrer"target="_blank",避免新窗口反制(reverse tabnabbing)。

2.3 响应式网格与分级标签样式

<style module> 部分定义了完整视觉系统(docs/es/pages/misc/sponsors.md):

类名 作用
.sponsorCloudGrid CSS Grid 布局,移动端 repeat(2, minmax(0, 1fr)) 两列,左右 -1.5rem 负边距制造出血效果
.sponsorCloudImageWrapper 单张卡片容器,Flex 纵向居中,背景 rgb(156 163 175 / 0.05) 的浅灰卡片
.sponsorName 名称文字 -webkit-line-clamp: 2,最多显示两行并截断,固定高度 3rem
.tagSponsorPlatinum/Gold/Silver/Bronze/Backer 五档标签胶囊:Platinum 灰底黑字 #E5E7EB、Gold 琥珀底 #F59E0B、Silver 灰底 #9CA3AF、Bronze 深棕底 #854D0E、Backer 蓝底 #2563EB,均为 border-radius: 9999px 胶囊形

断点行为:@media (min-width: 640px) 时网格取消负边距并加 1rem 圆角、卡片内边距增至 2.5rem@media (min-width: 768px) 时列数从 2 列扩展为 4 列。由于使用了 CSS Modules($style.xxx),所有类名天然作用域隔离,不会污染 VitePress 主题样式。

3. 数据来源:sponsors.json 的结构

页面渲染的唯一数据源是 docs/data/sponsors.json,当前约 3400 行。其顶层结构以层级名为键、赞助商对象数组为值,示例(截取 backer 层级):

{
  "backer": [
    {
      "name": "Mesh Payments",
      "imageUrl": "https://images.opencollective.com/meshpayments/87e9336/logo.png",
      "description": "Mesh Payments cardless solution...",
      "tier": "backer",
      "slug": "meshpayments",
      "website": "https://meshpayments.com/?utm_source=axios_docs_website&...",
      "twitter": "https://twitter.com/meshpayments?utm_source=axios_docs_website&...",
      "active": false
    }
  ]
}

各字段含义:

字段 类型 说明
name string 赞助商名称,缺失时脚本兜底为 'Backer'
imageUrl string | null Open Collective 上配置的 Logo 地址
description string | null 赞助商简介(页面当前未展示,仅存档)
tier string 层级:platinum / gold / silver / bronze / backer
slug string Open Collective 唯一标识,用于去重与活跃度匹配
website / twitter string | null 主页与推特地址,均已被追加 UTM 跟踪参数
active boolean 是否为当前生效的月度订阅赞助商

4. 数据管道:process-sponsors.js 脚本全解

sponsors.jsondocs/scripts/process-sponsors.js 生成。该脚本使用 axios 自身作为 HTTP 客户端(文档站依赖中声明 "axios": "^1.15.2"),向 Open Collective 的 GraphQL v2 API 发起两次 POST 查询:

4.1 两条 GraphQL 查询

  • 全量会员查询getAllSponsorsQuery):以 axios 的 GitHub 账号定位 Open Collective 账户,取 members(role: BACKER, limit: 1000),返回每个会员的 account(名称、slug、社交链接、Logo、简介)、tier.nametotalDonations 与入会时间 since
  • 活跃订阅查询getActiveSponsorsQuery):取 orders(onlyActiveSubscriptions: true, onlySubscriptions: true, frequency: MONTHLY, status: ACTIVE, limit: 1000),即当前正在按月订阅的赞助订单。

两条查询互补:前者覆盖所有历史赞助者,后者标记谁是“当前活跃”赞助商。

4.2 本地配置项 config

脚本顶部定义了特殊处理配置(docs/scripts/process-sponsors.js):

const config = {
  legacyAgreements: {
    Stytch: 'gold',
    Airbnb: 'silver',
    Descope: 'gold',
    'Principal Financial Group': 'gold',
  },
  sponsorsToIgnore: ['axios'],
  additionalSponsors: [ /* 手工登记的额外赞助商,含已处理好的 UTM 链接 */ ],
};
  • legacyAgreements:历史协议赞助商,层级不受 Open Collective 订阅档位约束,直接按此表强制指定(如 Stytch 固定为 gold)。层级解析优先级为:legacy 协议 > silver sponsor 档位映射 > 档位名小写 > 兜底 backer
  • sponsorsToIgnore:过滤掉 axios 组织自身的账户,避免自我赞助出现在页面上;
  • additionalSponsors:手工登记的 API 数据之外的赞助商,其 website 字段在配置里已预置完整的 utm_source=axios_docs_website 跟踪参数。

4.3 主流程:合并、去重、打标

mainProcess 的合并逻辑(docs/scripts/process-sponsors.js):

  1. 格式化全量数据formatAllSponsorData 先调用 selectLatestSponsorsBySlugslug 去重,保留每个赞助商“最近一次会员记录”,再映射为统一字段结构。去重规则(见其 JSDoc):有效时间戳优先于无效时间戳;双方时间戳都无效时保留先出现的一条。该实现是纯函数 + reduce 累加 Map,便于单测与复用;
  2. 活跃度打标:以 slug 为键比对活跃订阅列表,给全量数据中每个赞助商打上 active: true/false
  3. 补差:只存在于活跃订阅列表、不在全量会员中的赞助商,单独补充进结果并标记 active: true
  4. 注入手工赞助商additionalSponsors 按其 tier 归入对应分组,强制 active: true
  5. 落盘fs.writeFileSync('./data/sponsors.json', JSON.stringify(sponsorsByTier, null, 2)),按层级分组写入,即页面读取的 docs/data/sponsors.json

所有异常统一走 printErrorMessage(输出 Error: failed to process sponsors!),日志函数定义在 docs/scripts/utils.js,使用 chalk 着色输出 Success: / Info: / Error: 三类消息。

5. 运行与更新流程

仓库提供了两层脚本入口:

docs 子项目内docs/package.json):

cd docs
npm install                          # 会触发 postinstall: patch-package(应用 .vitepress 主题补丁,如 docs/patches 下的 splide 补丁)

npm run docs:update:sponsors         # 等价于 node ./scripts/process-sponsors.js,拉取并重新生成 data/sponsors.json
npm run docs:dev                     # vitepress dev,本地预览文档站(可实时看到赞助商页效果)
npm run docs:build                   # 先跑搜索文本测试(test:search)再 vitepress build
npm run prod:build                   # 生产构建:先 docs:update:sponsors 再 docs:build,保证上线数据最新

仓库根目录package.json):

npm run docs:dev    # 等价于 cd docs && npm run docs:dev

两个值得注意的构建细节:docs:build 依赖 test:search(对 docs/.vitepress/tokenizeSearchText.test.jsnode --test),把搜索文本分词质量作为构建门禁;prod:build 将赞助商数据刷新嵌入生产构建流程,意味着线上文档站的赞助商页数据每次发布都会重新拉取,而非依赖提交进仓库的静态快照。

6. 小结

赞助商页面虽然文案极少,但它是文档站中典型的数据驱动页面:sponsors.json 是唯一的展示事实来源,由 process-sponsors.js 通过两条 GraphQL 查询(全量会员 + 活跃订阅)合并生成,其间包含 legacy 层级覆盖、slug 去重、UTM 链接改写与人工补充赞助商等特殊处理;页面端则用 Vue <script setup> 完成层级拍平与动态 class 绑定,配合 CSS Modules 实现从两列到四列的响应式 Logo 墙。理解这条“GraphQL → 清洗脚本 → JSON → Vue 渲染”的链路,可以直接迁移到任何需要把第三方赞助数据可视化到 VitePress 文档站的场景。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384