Phosphor Icons 官网源码拆解:URL 即存储、水波动画与 7362 Star 图标库的架构实践

你肯定干过这事。在一个图标网站搜了半天,调好颜色和字重,找到第 7 页那个心仪的图标,手一滑刷新了页面。全没了。或者想发给同事,只能截一张图,让他自己再搜一遍。

phosphoricons.com 没有这个问题。你在上面做的每一次搜索、选的每一种颜色、换的每一个字重,都会实时出现在浏览器网址栏里。刷新,状态还在。复制链接发给同事,他打开就是你离开时的样子,搜索词、颜色、字号,一个不差。

大家好,我是若风。今天拆的是这个网站背后的仓库,phosphor-icons/homepage,7362 Star。说实话这个仓库在 GitHub 上有点特别,它是一个图标库组织里 star 最高的仓库,但真正干活的代码不在这。带着这个疑问往下看,你会看到一帮人能把一个「官网」较真到什么程度。

先搞清楚,这到底是个什么仓库

Phosphor Icons 是 2020 年开始的图标项目,Tobias Fried 和他的搭档两个人设计,全家 MIT 协议,代码和图标都随便商用,不用署名。它的卖点是 1248 个图标,每个都有 6 种字重,Thin、Light、Regular、Bold、Fill、Duotone,算下来 7488 个变体,全部在 16px 的网格上设计,小字号下依然清晰。

这个项目是一个多仓库组织,分工很清楚。@phosphor-icons/core 是唯一的数据源,装着所有图标的 SVG、名字、标签、Unicode 码点,371 Star。往下是一圈框架适配包,react 包 1740 Star,web 包 529 Star,还有 vue、flutter、elm、swift 等十来个。

有意思的地方来了。

官网仓库拿了 7362 Star,比 core 包和所有框架包都高。你想想看,一个组织的门面仓库,star 是干活仓库的 20 倍。这不是因为代码写得好,是因为它是「入口」。大部分人搜到 Phosphor,第一站就是这个网站,顺手就点了 star。入口拿走了几乎全部的注意力,这是开源组织里很普遍但很少被说破的现象。

官网也是 SDK 的第一个用户

拆开 package.json,你会看到一个反直觉的事实。这个官网依赖的是 @phosphor-icons/core@phosphor-icons/react,就是你 npm install 的那两个公开包,版本号都跟着 npm 走。

也就是说,官网自己没有私享通道。图标数据从 core 包来,渲染用 react 包的组件,任何一个开发者遇到的问题,官网代码自己会先遇到一遍。这种把自己狗粮吃到极致的架构,接口设计有没有偷懒,第一时间暴露在自家官网上。

src/lib/icons.ts 里那几行代码把这件事说得很直白,它从 core 包读出图标数据数组,然后逐个映射到 react 包的组件上,一共就十来行。官网的图标渲染层,和你项目里的渲染层,是同一份代码。

技术栈也是 2026 年的主流配置,React 19、Vite 7、TypeScript、Zustand 管状态、motion 做动画、fuse.js 做模糊搜索。没有 SSR,没有元框架,一个纯客户端 SPA,17 个组件,每个组件一个目录,CSS 文件和组件放在一起。

状态不进 localStorage,进网址栏

说真的,这是整个仓库最值得偷的一手。

大多数网站保存用户状态的方式是 localStorage,简单直接,但有个致命缺陷,状态和浏览器绑死,换个设备就没了,更没法分享。Phosphor 的做法是把状态直接写进 URL 的查询参数,?q=heart&color=hotpink&weight=fill&size=48 这样的形式。

实现层在 src/state/index.ts。Zustand 自带一个 persist 中间件,本来的用途是把 store 存到 localStorage。但他们给中间件塞了一个自定义的存储适配器 searchParameterStorage,把 getItem 和 setItem 的读写目标从 localStorage 换成了 URLSearchParams

状态一变,适配器就把字段写回网址栏,用的是 window.history.replaceState,不产生浏览器历史记录,不会把你后退键搞乱。刷新或分享这个链接时, initialState 函数再从 URL 解析出全部状态。

有一个细节特别见功力。写 URL 之前,适配器会判断值是不是默认值,是默认值就直接从参数里删掉。字重是默认的 Regular,URL 里就不出现 weight。颜色是默认黑色,就不出现 color。于是默认状态下网址干干净净,只有你真正改过的东西才会留下痕迹。

这个模式可以复用到很多地方。你做任何「配置型」的页面, playground、报表、调试工具,都值得问一句,这个状态能不能上 URL。上了 URL,刷新不丢,分享即还原,回退键天然可用,还省掉了 localStorage 的跨域和清理问题。

图中这条双向通道就是全部机制,左边进右边出,中间那个适配器是唯一需要自己写的代码。

1248 个图标,一次水波

打开官网搜一个词,搜索结果出来的那一瞬间,图标不是齐刷刷一起出现,而是像水面涟漪一样从左上角荡开,一圈一圈浮现。这是整个网站最有记忆点的动效,实现它的是一个逐像素计算的巧思。

代码在 src/components/IconGrid/IconGridItem.tsx,核心就一个常量,delayPerPixel = 0.0003

每个图标项在 useLayoutEffect 里测量自己的 offsetTopoffsetLeft,第一个图标的位置被记为原点。然后在 useEffect 里,每个图标算自己和原点的欧氏距离,距离乘以 0.0003,得到自己的入场延迟。离左上角越远的图标,出现得越晚。

为什么测量放 layoutEffect 而不是 effect?源码里留了注释,测量必须发生在布局周期,这样后面计算距离时,所有元素都已经量完了。这两行的时序差,就是这个动效不抖动的关键。

把这条计算路径画出来,是这样三步。

三步之外没有魔法,延迟算出来,剩下的交给 motion 的透明度过渡。

坦白讲这个方案性能上并不便宜。1248 个图标项全部渲染成 motion.button,没有任何虚拟化,没有窗口化裁剪,全量上。在低配设备上首帧成本不低,这是它为了动效美感付出的代价,后面细说。

搜索这件事,他们做了三层缓冲

搜索框看似简单,这个仓库把它做成了三层结构。

第一层是索引。fuse.js 实例在 src/state/index.ts 里全局创建一次,配置里给 name 字段设了 4 倍权重,threshold 卡在 0.2,还开了 useExtendedSearch。最后这个开关有意思,它让搜索语法支持前缀匹配,于是官网上搜 *new* 会精确命中所有带新标签的图标,*updated* 同理,这是藏在搜索框里的彩蛋语法。

第二层是防抖。src/components/SearchInput/SearchInput.tsx 里用自定义 useDebounce hook 把输入延迟 500ms 才真正触发搜索,避免每敲一个字就把 1248 个图标过滤一遍。同时绑了 Cmd+K 或 Ctrl+K 快捷键聚焦搜索框,老规矩了。

第三层最容易被忽略,输入法兼容。其实吧,英文世界根本意识不到这个问题的存在。2026 年 2 月有一个提交,「support IME composition in input」。中文用户用拼音输入法打「心」字,要按好多键,中间会经历「xin」这样的组合态。没有 IME 处理的搜索框,会把你正在拼的拼音当成搜索词直接过滤,页面闪来闪去。这个修复对英文用户完全无感,但对中日韩用户是实打实的体验分。

搜索命中后还有个小动作,页面平滑滚动到 id 为 beacon 的锚点,那个元素就放在网格顶部,搜索结果永远精准出现在你眼前。

不接受图标贡献的图标库

这个仓库最反直觉的地方在 CONTRIBUTING.md 的第一段,写得非常直接,由于项目的创造性属性,我们通常不接受图标贡献。

你想想看,一个开源图标库,不接受大家来画图标?但这正是 Phosphor 质量稳定的来源。1248 个图标出自两个人之手,风格的一致性、笔画的粗细、圆角的节奏,全部有人兜底。对比一下开放贡献的路线。

图标库 图标量 风格档位 设计网格 贡献模式 协议
Phosphor 1248 6 种字重 16px 内部设计 MIT
Lucide 1500+ 描边为主 24px 开放 PR ISC
Tabler 5000+ 描边加填充 24px 开放 PR MIT
Feather 287 单一描边 24px 2019 年后停更 MIT
Font Awesome 免费 2000 免费档单一 多种 内部加付费 免费 CC BY 4.0

开放 PR 的路线量大管饱,Tabler 五千多个图标什么都有,代价是风格一致性和评审成本的持续拉扯。Feather 走到另一头,作者停更后社区只能 fork 出 Lucide 续命。Phosphor 选了第三条路,设计收权,工程放权,代码和适配包谁都能改,图标本身两个人说了算。

还有一点差异值得注意。Font Awesome 的免费层是 CC BY 4.0,理论上要求署名,商用场景总归要留个心眼。Phosphor 从图标到代码全 MIT,一行署名都不用留,这对商业项目是最省心的选项。

藏在代码注释里的债

夸了这么多,说说不体面的部分。这些不是我从 issue 区翻出来的,是读代码时自己撞见的。

SearchInput.tsxhandleCancelSearch 函数里,有一行被注释掉的 setQuery(""),上面留着作者的注释,大意思是「应该取消挂起的防抖并立即清空搜索,不要造成卡顿」。翻译一下,清空搜索这个操作现在也要走 500ms 防抖,你点了清除按钮,页面会迟钝半秒才变空。作者知道这个问题,注释都写好了,但这个分支至今没修。

首页那 1248 个 motion.button 全量渲染的方案,前面提过代价。React 19 加上现代设备能扛住,但它注定和低端手机、长列表优化这些话题绝缘。这不算 bug,是一个明确的取舍,动效优先。

issue 区倒是能看出用户的真实痛点。#481 有 8 条讨论,标题是「用字符串名动态渲染图标」,这是 React 用户的经典困境,tree-shaking 要求静态导入,但业务里经常只有一个字符串名字。8 条讨论里没有官方给出优雅方案,因为这个问题本质上和 tree-shaking 是对抗的,不是官网能修的。#508 则是个更朴素的功能 bug,排序图标的升序降序语义弄反了,9 条讨论挂了很久。

版本节奏也值得一提。release 页面停在 2024 年 3 月的 v2.1.0,看起来像弃坑,但提交记录一路活跃到 2026 年 8 月底,前几天还在修 footer 的死链。这个项目的 release 语义是「图标数据的大版本」,不是常规的工程发布,看 star 和 release 判断项目死活,在这个仓库上会得出完全错误的结论。

七千 Star 的官网,跑在虚拟主机上

最后一个意外在部署环节。

.github/workflows/dreamhost-static.yaml 揭晓答案,GitHub Actions 构建静态产物,然后装上 sshpass,用 rsync 推到 DreamHost 的共享虚拟主机上。不是 Vercel,也不是 Netlify,是那种十几年前买域名送的传统虚拟主机。

你猜怎么着,这网站跑得挺好。一个纯静态 SPA,构建产物 rsync 上去就完事,虚拟主机一年几十块钱,没有任何 serverless 的花活。技术选型新旧不重要,匹配才重要。

仓库里还有个 scripts/generate.ts 也印证了这种朴素,它从 core 包里读 SVG 源文件,生成 Nucleo 和 IconJar 两款设计工具的导入包,设计师下载一次,离线用。代码不超过三百行,一个抽象 Exporter 类加两个实现,没有构建管线,没有缓存层,跑一次生成一次。

图纸只有一份

拆完这个仓库,有两样东西值得带走。

第一样是一个架构判断,我管它叫「图纸只有一份」。core 包是唯一真源,官网、react 包、vue 包、IconJar 导出器,全是这张图纸的下游印刷厂。任何修改变更只发生在一处,然后向所有出口扇出。很多团队做设计系统会栽在多份真源上,设计稿一份、代码一份、官网展示一份,三处慢慢漂移。Phosphor 用包依赖把漂移的可能性物理消灭了,官网用的就是 npm 上那个版本,想不同步都难。

第二样是「状态上 URL」的模式。任何一个有配置态的页面都值得重新审视一遍,你的状态放在 localStorage 里,就是放在一台设备里。放上 URL,它就变成了可以复制、可以收藏、可以发给同事的一等公民。实现成本被 Zustand 的 persist 中间件降到了一个适配器函数。

一个图标官网而已,7362 Star 拿得其实不冤。它把一个本来可以做成静态展板的东西,做成了自己架构理念的最佳演示。下次你调一个开源项目的官网,不妨多看一眼它用的是不是公开包,状态存的是不是 URL,这两个细节里藏着维护者的工程品味。

相关推荐
BigTopOne30 分钟前
WebRTC Native / Android 开发学习资源整理
前端
wangruofeng39 分钟前
AI 画架构图总差点意思,archify 给它加了一条验收流水线
架构·github·aigc
叶落方知秋1 小时前
大模型学习笔记:排序怎么帮公司赚钱、多方怎么博弈
架构
excel1 小时前
nuxt 3 升级 nuxt 4 报错之 hasInjectionContext
前端
何以解忧,唯有..2 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain
叶落方知秋2 小时前
大模型学习笔记:模型怎么练出来、数据怎么存起来
架构
叶落方知秋2 小时前
大模型学习笔记:AI 学什么数据,决定了它能有多聪明
架构
软件聚导航3 小时前
「聚小软 AI 助手」技术升级:从数据库检索到 RAG 知识库问答
前端·数据库·mysql·微信小程序·小程序·ai编程·rag
林澈在路上3 小时前
AI翻唱软件哪个好 2026国产AI写歌工具对比推荐
大数据·人工智能·深度学习·github·aigc·音视频·音频