【免费下载链接】boneyard
Auto generated skeleton loading framework
导读
本文以 boneyard 仓库中《Boneyard Skeleton Skill》文档为主体,系统讲解boneyard-js——一个把真实 UI 快照成带定位矩形 "bones"(骨骼)的骨架屏生成器。你将掌握:bones 数据格式与解析优先级、boneyard.config.json全部配置项、CLI 构建与 Vite 插件工作流、React/Svelte/Vue/Angular/Preact/React Native 多框架接入方式,以及一套可直接照做的调试检查清单。读完即可为项目接入"像素级还原真实布局"的骨架屏,无需手写任何描述符。
一、架构总览:从真实 DOM 到骨架矩形
boneyard-js的核心思想是快照真实渲染后的 UI,将其转换为绝对定位的矩形占位元素。所有核心源码位于 packages/boneyard/:
| 文件 | 职责 |
|---|---|
| src/react.tsx | React 的<Skeleton>组件,同时导出configureBoneyard、registerBones、BoneSuspense |
| src/preact.tsx | Preact 原生集成(无需 compat 层) |
| src/Skeleton.svelte | Svelte 5 组件 |
| src/Skeleton.vue | Vue 组件 |
| src/angular.ts | Angular 组件 |
| src/native.tsx、src/react-native.tsx | React Native 支持 |
| src/extract.ts | snapshotBones()DOM 遍历器、fromElement()描述符提取器 |
| src/shared.ts | bones 注册表、动画常量(SHIMMER/PULSE/DEFAULTS)、resolveResponsive |
| src/types.ts | SnapshotConfig、Bone、CompactBone、ResponsiveBones等类型定义 |
| src/runtime.ts | 非 React 场景的 vanillarenderBones() |
| src/layout.ts | 描述符驱动的compileDescriptor/computeLayout布局引擎 |
| bin/cli.js | CLI 入口(boneyard-js build) |
多框架导出入口
根据 packages/boneyard/package.json 的 exports 字段,包提供多种子路径,全部共享同一套 bones 数据与动画常量:
boneyard-js— 导出snapshotBones、renderBones、fromElement、computeLayout等核心 API(见 src/index.ts)boneyard-js/react—Skeleton、registerBones、configureBoneyardboneyard-js/preact—Skeleton、registerBones、configureBoneyardboneyard-js/native—Skeleton、registerBones、configureBoneyard(React Native)boneyard-js/svelte—Skeleton组件、registerBonesboneyard-js/vue—Skeleton组件、registerBones、configureBoneyardboneyard-js/angular—SkeletonComponent、registerBones、configureBoneyardboneyard-js/vite—boneyardPlugin()Vite 插件
二、Bones 数据格式与解析优先级
2.1 紧凑数组格式
snapshotBones()产出(或 CLI 写入*.bones.json的)是紧凑数组:
[x%, y_px, w%, h_px, borderRadius, isContainer?]x和w是相对于容器宽度的百分比;y和h是像素值;borderRadius可以是数字(px)或字符串(如"50%");- 第 6 个可选元素
isContainer(真值)标记容器骨骼——它在渲染时会被跳过。容器骨骼代表父级背景,若与子骨骼同时渲染会产生透明度叠加。
在 src/types.ts 中,CompactBone被定义为 5 元或 6 元元组,normalizeBone()负责把紧凑元组校验(长度必须为 5 或 6)并归一化为结构化Bone对象;同时兼容resolveJsonModule下从 JSON 导入时被收窄为数组的类型情况。仓库示例 packages/boneyard/src/bones/sidebar-nav.bones.json 展示了结构化形式:每个断点下包含name、viewportWidth、width、height与bones数组,例如{ "x": 8, "y": 9, "w": 28, "h": 28, "r": "50%" }即一个圆形头像骨骼。
2.2 骨骼解析优先级
<Skeleton>在运行时按以下优先级获取骨骼数据(React 实现见 src/react.tsx 的initialBones ?? getRegisteredBones(name)):
- 显式传入的
initialBonesprop(优先级最高); - 按
name在注册表中查找(来自registry.js,由registerBones注入,底层是 src/shared.ts 的Map<string, RegisteredBones>); - Fixture 兜底——仅在 CLI 构建模式(
window.__BONEYARD_BUILD === true)下生效。
2.3 响应式断点选择
多断点 bones 由ResponsiveBones({ breakpoints: Record<number, SkeletonResult> })表示。resolveResponsive()(见 src/shared.ts)实现"就近匹配":把所有断点键升序排序后,从大到小找到第一个width >= bp的断点;若没有命中则回退到最小断点。<Skeleton>默认用容器实测宽度(container-query 式行为)来选择断点,也可通过select: 'viewport'改用window.innerWidth——这与 CLI 按视口宽度建档的方式一致,适合应用壳布局(容器窄于窗口)的场景。
三、动画常量与暗色模式
3.1 动画常量(单一事实来源)
所有框架实现都从 src/shared.ts 导入动画常量,保证跨框架行为一致:
SHIMMER = { angle: 110, start: 30, end: 70, speed: '2s', lightHighlight: '#f7f7f7', darkHighlight: '#2c2c2c' } PULSE = { speed: '1.8s', lightAdjust: 0.3, darkAdjust: 0.02 } DEFAULTS = { web: { light: '#f0f0f0', dark: '#222222' }, native: { light: '#f0f0f0', dark: '#222222' } }SHIMMER:扫光动画的角度(110°)、渐变的起止位置(30%→70%)、时长(2s)及明暗两套高光色;PULSE:呼吸动画时长 1.8s,lightAdjust/darkAdjust是adjustColor()在明暗模式下对基色做"提亮"的系数(源码中adjustColor(color, amount)同时支持rgba()与#hex两种颜色输入,见 src/shared.ts);CONTAINER:容器骨骼专用的调色系数(adjustment: 0.12/darkAdjustment: 0.03)。
adjustColor是动画实现的关键底层函数:pulse 动画在0%/100%用基色、50%用adjustColor(基色, 0.3)生成高亮帧(React 中以@keyframes bp-${uid}内联样式注入,见 src/react.tsx)。
3.2 暗色模式检测
暗色模式通过<html>或任意祖先元素上的.dark类检测(Tailwind 标准约定),不使用prefers-color-scheme,把控制权明确交给应用开发者。当.dark存在时启用darkColor与darkShimmerColor。React 实现用MutationObserver监听<html>的 class 变化、并监听matchMedia('(prefers-color-scheme: dark)')事件(用于 OS 主题切换触发祖先.dark变化的场景),见 src/react.tsx。
四、SnapshotConfig:控制骨骼提取的四个开关
snapshotConfig以 prop 形式传给<Skeleton>,或作为snapshotBones()的第三个参数,类型定义见 src/types.ts:
{ leafTags?: string[] // 视为原子骨骼的标签(与默认值合并:p,h1-h6,li,td,th) captureRoundedBorders?: boolean // 捕获带边框+圆角但无背景的元素(默认: true) excludeTags?: string[] // 完全跳过这些标签 excludeSelectors?: string[] // 跳过匹配 CSS 选择器的元素 }leafTags:与默认集合合并而非替换。默认集合在 src/extract.ts 定义为p, h1-h6, li, td, th。命中 leafTags 的节点被当作单块扁平骨骼,不再递归其子节点;captureRoundedBorders:默认true。在 src/extract.ts 中,容器只要满足"背景色非透明 / 有背景图 / 有可见边框且圆角"之一即判定为有视觉表面(hasVisualSurface),从而产出容器骨骼。这正是"白色卡片(bg-white rounded-xl border)也能被捕获"的原理——每个背景色都算数,白卡也是卡;excludeTags/excludeSelectors:命中的元素连同所有后代一起跳过(src/extract.ts 中直接return不递归)。excludeSelectors支持任意合法 CSS 选择器:类、ID、属性、标签及组合选择器(如'.card .badge')。
底层snapshotBones()的遍历逻辑(src/extract.ts)值得一提:
- 读取每个可见元素的
getComputedStyle,跳过display:none、visibility:hidden、opacity:0; - 图片/表单元素(
img/svg/video/canvas/input/button/textarea/select)一律视为叶子; - 叶子骨骼取
getBoundingClientRect()的精确像素位置,x/w换算为相对根元素的百分比,y/h保持像素; - 近正方形的媒体元素(宽高差 < 4px)自动得到
50%圆角(适配 SVG 图标、圆形头像); - 表格节点(
tr/td/th/thead/tbody/table)强制r: 0,避免与overflow:hidden父级冲突。
五、配置文件:boneyard.config.json 全参数详解
boneyard.config.json是首要定制点,同时控制 CLI 构建与运行时默认值。运行时选项会通过configureBoneyard()烘焙进生成的registry.js。完整示例:
{ "breakpoints": [375, 768, 1280], "out": "./src/bones", "wait": 800, "color": "#e5e5e5", "darkColor": "#2a2a2a", "animate": "shimmer", "shimmerColor": "#ebebeb", "darkShimmerColor": "#333333", "speed": "2s", "shimmerAngle": 110, "stagger": false, "transition": false, "boneClass": "", "resolveEnvVars": true, "auth": { "cookies": [{ "name": "session", "value": "env[SESSION_TOKEN]", "domain": "localhost" }], "headers": { "Authorization": "Bearer env[API_TOKEN]" } } }5.1 构建期选项
| Key | 默认值 | 说明 |
|---|---|---|
| breakpoints | [375, 768, 1280] | CLI 捕获的视口宽度(可自动探测 Tailwind 断点) |
| out | ./src/bones | 输出目录 |
| wait | 800 | 页面加载后等待的毫秒数,再开始捕获 |
5.2 运行时选项(烘焙进 registry.js)
| Key | 默认值 | 说明 |
|---|---|---|
| color | #f0f0f0 | 骨骼填充色(亮色模式) |
| darkColor | #222222 | 骨骼填充色(暗色模式,.dark类) |
| animate | "pulse" | 动画:"pulse"、"shimmer"或"solid" |
| shimmerColor | #f7f7f7 | Shimmer 高光色(亮色模式) |
| darkShimmerColor | #2c2c2c | Shimmer 高光色(暗色模式) |
| speed | "2s"(shimmer)/ "1.8s"(pulse) | 动画时长 |
| shimmerAngle | 110 | Shimmer 渐变角度(度) |
| stagger | false | 骨骼间动画延迟毫秒数(true = 80ms) |
| transition | false | 加载结束时的淡出过渡毫秒数(true = 300ms) |
| boneClass | — | 应用到每个骨骼元素的 CSS 类 |
5.3 认证与会话支持
auth配置让 CLI/Vite 插件能携带 Cookie 与请求头访问登录态页面,支持env[VAR_NAME]占位符(配合resolveEnvVars: true从.env与process.env解析,缺失时警告并解析为空串,避免静默发送坏 token)。Vite 插件的实现细节见 src/vite.ts:Cookie 会通过白名单ALLOWED_COOKIE_KEYS过滤,请求头会拦截host、content-length等危险头。此外插件还支持cdp选项直连已有 Chrome 调试端口,复用用户浏览器里的真实登录态(src/vite.ts)。
5.4 优先级
组件级 props > 配置文件(经configureBoneyard())> src/shared.ts 包默认值。CLI 标志(flags)对构建期选项会覆盖配置文件。React 端configureBoneyard({...})会把配置合并进全局globalConfig(见 src/react.tsx),组件 props 再覆盖之。
六、实战任务:接入、Fixture、排除与 CLI
6.1 给组件添加骨架屏
import { Skeleton } from 'boneyard-js/react' <Skeleton name="my-component" loading={isLoading}> <MyComponent data={data} /> </Skeleton>运行时loading={true}时展示骨骼层,loading={false}时展示真实 children;骨骼层以绝对定位覆盖层渲染,并通过aria-busy与data-boneyard-*属性保持可访问性与可调试性(见 src/react.tsx)。
6.2 使用 Fixture(构建期无真实数据时)
<Skeleton name="my-component" loading={isLoading} fixture={<MyFixture />} snapshotConfig={{ leafTags: ["section"] }} > <MyComponent data={data} /> </Skeleton>关键模式:在 fixture 中用<section>(或任意自定义标签)作为叶子元素,再把该标签加入leafTags,提取器就会把每个 section 当作单块扁平骨骼处理,不再递归其子元素。fixture只在 CLI 设置window.__BONEYARD_BUILD = true时渲染(src/react.tsx 的构建模式分支:fixture ?? children)。
6.3 排除不需要捕获的元素
方式一:标记属性
<nav><Skeleton snapshotConfig={{ excludeSelectors: ['.icon', 'svg'], excludeTags: ['nav'] }}>6.4 CLI 命令速查
# 自动探测开发服务器 npx boneyard-js build # 显式 URL + 输出目录 npx boneyard-js build http://localhost:PORT --out src/bones # 强制全量重建(跳过 hash 检查) npx boneyard-js build --force # Watch 模式(HMR 时重新捕获) npx boneyard-js build --watch # 自定义断点 npx boneyard-js build --breakpoints 375,640,768,1024,1280,1536 # React Native 模式 npx boneyard-js build --native --out ./bones6.5 Vite 插件(免第二个终端)
若项目使用 Vite,可直接接入boneyardPlugin()(src/vite.ts):dev server 启动即执行首次捕获,之后每次 HMR 变更在 1.5s 防抖后自动重新捕获,并生成/合并.bones.json与registry.*。插件支持out、breakpoints、wait、routes、skipInitial、cdp、debug等选项,且配置文件的优先级低于插件显式选项。其底层逻辑与 CLI 一致:注入__BONEYARD_BUILD = true、遍历[data-boneyard]标记、按data-boneyard-config读取 snapshotConfig、调用window.__BONEYARD_SNAPSHOT(即snapshotBones)完成捕获。
七、Skeleton Props 完整参考
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
loading | boolean | 必填 | 显示骨架还是 children |
children | ReactNode | 必填 | 真实内容 |
name | string | 必填 | 注册表 key + CLI 标识 |
initialBones | ResponsiveBones | — | 预生成骨骼(覆盖注册表) |
color | string | #f0f0f0 | 骨骼填充色(亮色模式),任意 CSS 颜色(hex、rgba、hsl 等) |
darkColor | string | #222222 | 暗色模式骨骼填充色(.dark类),任意 CSS 颜色 |
animate | AnimationStyle | 'pulse' | "pulse"、"shimmer"、"solid"(也接受 boolean,true=pulse、false=solid) |
stagger | number \| boolean | false | 交错延迟(true=80ms) |
transition | number \| boolean | false | 淡出时长(true=300ms) |
boneClass | string | — | 每个骨骼的 CSS 类 |
className | string | — | 容器类 |
fallback | ReactNode | — | loading 且无骨骼时显示 |
fixture | ReactNode | — | 供 CLI 捕获的 mock 内容 |
snapshotConfig | SnapshotConfig | — | 控制骨骼提取 |
select | 'container' \| 'viewport' | 'container' | 断点选择的宽度基准 |
React 还额外提供BoneSuspense(src/react.tsx):与 React 的<Suspense>模型配合,对使用useSuspenseQuery或React.lazy的组件,在挂起时自动展示<Skeleton loading>;构建模式下会把 children 包进<Suspense>,避免挂起查询导致提取崩溃,--wait窗口内查询自然 resolve 后快照真实 DOM。
八、调试检查清单
- 骨架屏完全不显示:检查应用入口是否导入了
registry.js,且存在对应name的 bones JSON; - 骨骼过多 / 出现内部形状:在
snapshotConfig中添加leafTags后重建; - 骨骼与布局不匹配:用
--force从当前 DOM 重新生成; - 断点选错:检查容器宽度——骨骼使用就近的
<=断点匹配; - 暗色模式未生效:boneyard 依赖
<html>或祖先上的.dark类,不使用prefers-color-scheme; - CLI 找不到骨架:组件需要
loading={false}(或有 fixture)让真实 UI 渲染出来供捕获; - 透明度叠加 / 双重渲染:容器骨骼(
c: true)应在渲染时被跳过——如果出现叠加,说明过滤逻辑缺失(各框架实现均通过normalizeBone(raw).c过滤,见 src/react.tsx 与 src/runtime.ts); - Shimmer 不可见:检查
shimmerColor与color的对比度——默认是#f0f0f0上的#f7f7f7。
九、延伸:无 DOM 环境的描述符方案
除浏览器快照路径外,包还提供描述符驱动的布局引擎(src/layout.ts)与 vanilla 运行时renderBones()(src/runtime.ts):fromElement()把渲染后的 DOM 提取为SkeletonDescriptor(含 display/flex/gap/padding/aspectRatio/font/text 等结构化字段),compileDescriptor+computeLayout用编译缓存与文本测量在任意容器宽度下计算骨骼位置,渲染期无需 DOM,适用于 SSR、Worker、边缘函数等场景;skeleton(el)是"提取→计算→渲染"的一站式便捷函数(src/index.ts)。这两条路径与浏览器快照路径共享同一套 bones 格式与动画常量,是理解 boneyard 完整能力边界的补充。
十、最小落地三步走
- 构建:
npx boneyard-js build(或接入boneyardPlugin()),按需在boneyard.config.json中调整断点、动画、认证; - 接线:在应用入口导入生成的
registry.js(自动调用registerBones注册所有 bones JSON); - 包裹:
<Skeleton name="my-component" loading={isLoading}>按 name 自动解析骨骼,必要时用initialBones、fixture、snapshotConfig精确控制提取行为。
整个过程无需手写布局描述,boneyard 直接读取浏览器已计算好的像素位置,天然做到"像素级还原真实 UI",这也是其零布局偏移(zero-layout-shift)能力的基础。
【免费下载链接】boneyard
Auto generated skeleton loading framework
相关推荐
Boneyard 骨架屏框架开发指南:从真实 UI 快照提取 bones,到 CLI 构建、配置与多框架落地
Boneyard 骨架屏框架开发指南:从真实 UI 快照提取 bones,到 CLI 构建、配置与多框架落地 Boneyard 是一款以"快照真实 UI 为骨架
boneyard:从真实 UI 自动提取像素级骨架屏的跨框架方案
boneyard:从真实 UI 自动提取像素级骨架屏的跨框架方案 boneyard 是一套自动生成骨架屏(skeleton loading screen)的工程
TABAnimated:iOS原生骨架屏加载框架
TABAnimated:iOS原生骨架屏加载框架 1. 项目基础介绍及编程语言 TABAnimated 是一个为 iOS 开发者提供的原生骨架屏加载框架。该框架
移动开发UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考