最近看到一个开源库 sse-stuntman 的徽章独具一格:

就在想我的项目也能用上就好了。
平常我们的徽章都是固定的配色、固定 logo 甚至固定的文字。
而这个库的徽章新颖之处在于,logo 是自定义的(coverage 的 logo 是 CS),logo 颜色,dependencies 背景色黄色都是自定义。这是如何做到的呢?
平常我们用的最多的是 npm 下载量徽章,其实 downloads 徽章支持四种统计维度:
`dw`, `dm`, `dy`, `d18m` 最近 1 周 / 1 月 / 1 年 / 18 个月下载量
除了展示 npm 下载量还支持多达 73 项服务的下载量!

学完后我们甚至可以打造一个天气 badge。
徽章原理
我们从两个维度来说。
从能力来说:徽章(Badge)是 img.shields.io 公司提供的图片服务,分成静态徽章和动态徽章(后者依赖该公司服务端提供数据能力,比如 npm 的下载量)。
从格式来说:徽章其实是一段 markdown 代码,图片格式而已 。
当然 HTML 片段也是合法的 markdown,可以被 github 或掘金等平台支持,故也可以是
<img alt="[alt]" src="<src>" />(只有src必选,但建议加alt这样即使图片 broken 也能展示图片含义)。
徽章拆解
静态徽章
徽章分成两部分,path 和 query。
https://img.shields.io/badge/label-message-greenyellow 这种格式称为静态徽章,即里面的文字和颜色都是固定的,可以看出 path 包含三元素"label message color",这是我们可以定制的部分。
当没有动态服务可以满足时候就可以使用,比如 sse-stuntman 的依赖数量徽章,这个库以 0 依赖轻量级为特色,所以他加了一个 dependencies 0 的徽章。代码很简单:
arduino
https://img.shields.io/badge/dependencies-0-green
但是目前渲染的是还和 sse-stuntman 不一样,少了 logo 以及左半部分背景色不是黄色。
我们还需要学习更多"Query 查询参数"。下面文字来自官方文档。
静态徽章仅接收一个必填路径参数,该参数编码内容分为以下两种格式:
-
标签、提示文本、颜色(Label, message and color),三者以短横线
-分隔。示例:--- 对应地址:img.shields.io/badge/any_t...
-
仅提示文本与颜色(Message and color only),二者以短横线
-分隔。示例:--- 对应地址:img.shields.io/badge/just%...
| URL 传入字符 | 徽章展示字符 |
|---|---|
单下划线 _ 或 %20 |
空格 |
双下划线 __ |
单下划线 _ |
双短横线 -- |
单短横线 - |
这里的意思是如果你想在 label 里面增加空格,你得写
%20,以此类推。
更多静态徽章示例
- 样式(Style) :
--- 对应地址:img.shields.io/badge/build...
- 颜色(颜色名格式) :
--- 对应地址:img.shields.io/badge/cover...
- 图标(Logo) :
--- 对应地址:img.shields.io/badge/githu...
支持十六进制、RGB、RGBA、HSL、HSLA 色值以及 CSS 标准颜色名。 注意: 部分内置颜色名的色值与原生 CSS 标准色存在差异。完整颜色名列表可查阅 badge-maker 文档。
动态徽章
我们常见的动态徽章有 npm version 和 npm downloads。每一种动态徽章都有可交互 playground:

Dynamic badges 展示具体项目指标,目前 shields.io 为数百个服务提供了徽章。
动态徽章又分为内置服务和自定义。
内置服务是 shields.io 给开发者提供了现成能力,这是徽章具备的动态变化魔力的基础。
自定义又有 6 种格式。

动态 JSON 徽章
动态 JSON 徽章支持通过 JSONPath 选择器从任意 JSON 文档中提取任意字段值,并将该值展示在徽章上。
比如我们有一个 package.json url raw.githubusercontent.com/legend80s/p... 可以如此展示其 name query=%24.name 24% 是 $ 的转义:
举个更加有趣的例子,实时北京天气预报
我们可以展示北京当前温度(注意是天气数据不是写死的是实时的,每天打开都不一样)

体感温度:

把 temp_C 改成 FeelsLikeC 即可。
为什么知道这么写?
几个技术要点:
- url --- 必填。指向一份 JSON 文档
- query --- 必填。用于查询该 JSON 文档的 JSONPath 表达式。示例:$.name
对于北京天气 url 是 https://wttr.in/beijing?lang=zh-cn&format=j1,query 是 query=$.current_condition[0].temp_C,label 是 北京当前温度,摄氏度是通过 suffix=℃增加的。更多参数见 Dynamic JSON Badge。
Endpoint Badge
只要你的 your_api 返回如下格式就会展示对应内容。
json
{ "schemaVersion": 1, "label": "hello", "message": "sweet world", "color": "orange" }
有什么用呢?当无论静态还是内置服务都无法展示徽章内数据,因为这些数据可能存在你的服务器,就可以开放一个符合返回格式的 API endpoint。就是这么简单。
回顾下 Path 和 Query:
Path
/badge/ 都是静态 badge,动态 badge 服务不同 path 不同。举例:
-
npm 版本号 path:
/npm/v/<pkg_name>完整https://img.shields.io/npm/v/<pkg_name>示例:img.shields.io/npm/v/sse-s... 或 img.shields.io/npm/v/sse-s... 都行 -
npm 下载量 示例:img.shields.io/npm/dw/vue

Query
我们把 dependencies 0 徽章源码拿出来
ini
https://img.shields.io/badge/dependencies-0-green?logo=npm&logoColor=212121&labelColor=ffc44e
问号后面的就是 query
ini
?logo=npm&logoColor=212121&labelColor=ffc44e
现在可以看到 logo logoColor labelColor 如何定制。那么还有哪些参数呢?
| 参数 | 说明 |
|---|---|
style |
string 类型 可选值:flat、flat-square、plastic、for-the-badge、social 如未指定,此徽章默认样式为 flat 示例:flat |
logo |
string 类型 来自 Simple Icons 的图标标识(slug)。点击 simple-icons 上的图标标题即可复制其 slug,也可在 simple-icons 仓库的 slugs.md 文件中查找。 示例:appveyor |
logoColor |
string 类型 图标的颜色(支持十六进制、RGB、RGBA、HSL、HSLA 及 CSS 命名颜色)。仅适用于 Simple Icons 图标,不适用于自定义图标。 示例:violet |
logoSize |
string 类型 设置 auto 可使图标自适应缩放。适用于部分较宽的图标(如 amd、amg)。仅适用于 Simple Icons 图标,不适用于自定义图标。 示例:auto |
label |
string 类型 覆盖左侧默认文本(空格或特殊字符需要 URL 编码!) 示例:healthiness |
labelColor |
string 类型 左侧部分的背景颜色(支持十六进制、RGB、RGBA、HSL、HSLA 及 CSS 命名颜色)。 示例:abcdef |
color |
string 类型 右侧部分的背景颜色(支持十六进制、RGB、RGBA、HSL、HSLA 及 CSS 命名颜色)。 示例:fedcba |
cacheSeconds |
string 类型 HTTP 缓存时长(规则会自动为每个徽章推断默认值,低于默认值的设置将被忽略)。 示例:3600 |
link |
string[] 类型 指定点击徽章左侧/右侧时的跳转行为。注意:此功能仅在将徽章嵌入 <object> HTML 标签时有效,不适用于 <img> 标签或标记语言。 |
上面是通用参数,不同服务还有自己的参数,比如 npm version 可以指定 registry(公司内部包就可以派上用场了)。
md
| **`registry_uri`** | 示例:`https://registry.npmjs.com` |
定制与众不同的徽章
接下来我们把 sse-stuntman 这个库的徽章源码抠出来学习,然后复制到自己的 README,并定制成具有自己项目印记的小徽章(badge)。
md
<!-- https://github.com/legend80s/sse-stuntman/blob/main/README.md -->
<p>
<a href="https://www.npmjs.com/package/sse-stuntman" target="_blank">
<img src="https://img.shields.io/npm/v/sse-stuntman.svg?logo=npm&logoColor=cyan" alt="npm version" />
</a>
<a href="https://www.npmjs.com/package/sse-stuntman?activeTab=dependencies" target="_blank">
<img alt="0 dependencies" src="https://img.shields.io/badge/0-green?logo=npm&logoColor=212121&labelColor=ffc44e&label=dependencies&color=green&color=212121" />
</a>
<a href="https://www.npmjs.com/package/sse-stuntman">
<img src="https://img.shields.io/npm/dm/sse-stuntman.svg?logo=npm&logoColor=cyan" alt="npm downloads" />
</a>

</p>
掘金也支持,比如下面的徽章就是上述代码渲染的,注释不是我截图,是实实在在通过 HTML 代码渲染的,是动态的,当 sse-stuntman 的下载量变化了,你再来这篇文章看,会发现数字是事实同步的。
这就是 badge 的魔力。它不仅仅是一个静态的图片。
前端开发者特别容易理解

但是上述有个问题,就是 badge 之间会有下划线,这是因为 <a /> 链接导致的,改成 markdown link 语法即可:
ruby
<p>
[](https://www.npmjs.com/package/sse-stuntman)
[](https://www.npmjs.com/package/sse-stuntman?activeTab=dependencies)
[](https://www.npmjs.com/package/sse-stuntman)

</p>
注意此种写法第一个 bage 和 <p> 必须留有至少一个空行,否则 badge 将展示成源码而非图片。
还可以优化下,让 badge 居中 <div align="center">。其实这个技巧也可以用在掘金里面。
ini
<div align="center">
[](https://www.npmjs.com/package/sse-stuntman)
[](https://www.npmjs.com/package/sse-stuntman?activeTab=dependencies)
[](https://www.npmjs.com/package/sse-stuntman)

</p>
本系列每 1 个观点都追溯到 1 行源码、1 段官方文档。