拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

JSON驱动Sketch设计稿:json-sketchapp的落地实践与避坑指南

JSON驱动Sketch设计稿:json-sketchapp的落地实践与避坑指南 简介json-sketchapp是一款面向Sketch设计师与前端开发者的实验性插件核心功能是将结构化JSON文件直接转换为Sketch可编辑的设计稿。插件基于skpm工具链开发适合熟悉JavaScript、希望打通数据与设计稿链路的进阶用户可用于批量生成界面原型、数据可视化草稿等场景。压缩包共9个文件约81KB以JSON配置与示例数据为主辅以JS插件逻辑、Markdown说明文档及图标资源结构紧凑便于快速阅读源码与二次修改。已有671人学习/下载。通过源码可了解skpm插件的基本工程结构、manifest.json插件注册方式、JSON到Sketch对象的映射思路以及npm run build/watch等开发调试流程README中对插件工作原理也有说明适合作为Sketch插件开发的入门参考。1. json-sketchapp 是什么用 JSON 驱动设计稿省掉拖拽画布后台管理系统改版四十多个信息看板要从瞎凑的数据改成真实接口字段UI 出图早就不够用了我得自己在 Sketch 里一个个拖形状、对坐标、贴数据。拖到第十二个页面的时候我就明白了一件事这类重复劳动不该用鼠标完成。json-sketchapp 就是为这个场景准备的 Sketch 插件它把设计稿的描述从画布挪进 JSON 文件里你写的是一份结构化的 json 格式数据插件负责把它翻译成 Sketch 文件里的图层树。于是改布局、换文案、批量生成同构页面都从「重新画一遍」变成「改 JSON 再跑一次」。适合被数据驱动的界面稿反复折磨的设计师和前端也适合所有想把 json 转换当作构建流程一环的工程师。这篇文章我不讲安装包的点击过程讲的是它背后的文件结构和一套能复现的落地路径以及我在真实项目里踩过的坑。2. 先看 .sketch 文件的底细一个 zip 包里面全是 json 格式的文档很多人在第一次接触 json-sketchapp 时会有一个误区想搞清楚插件是怎么把 JSON「画」成图形的。其实反过来想更清楚——.sketch 文件本身就是一堆 JSON 的压缩包插件只是把这些 JSON 重新拼装进正确的位置。理解这一点你才能把转换过程从「黑匣子调用」变成「可控的数据映射」。2.1 拆开 .sketch 看结构document.json、pages/xxx.json、meta.json最常见的做法是直接手动拆包找一个随便生成的 .sketch 文件在终端里执行cp demo.sketch demo.zip unzip demo.zip -d demo_unpacked tree demo_unpacked你会看到类似这样的结构demo_unpacked/ ├── document.json ├── meta.json ├── user.json └── pages/ └── 0123ABC-xxxx.json逻辑说明Sketch 文件本质是按照固定目录结构压缩的 zipped 文件不是某个私有二进制格式。document.json 负责页面索引和文档级设置pages 目录下每个 JSON 对应一个画板页面meta.json 记录版本和兼容信息。改 .sketch 后缀为 .zip 再解压是验证插件输出是否正确的最直接手段。参数说明注意这里的命名没有固定规则页面文件名是一长串 UUID不要靠猜用 document.json 里的页面 id 去关联。还有一点解压后立刻用jq .pages document.json看看页面数量能帮你快速确认文件里到底有没有内容。2.2 为什么 JSON 能还原出设计稿图层树与坐标系统Sketch 的页面 JSON 不是一张扁平图片的二进制它保存的是完整的图层树。一个组group里面有若干子图层每个图层有 class、frame、style 等字段。frame 里的 x、y 表示相对父层的坐标width、height 决定尺寸文字层额外带 attributedString 属性。插件做的事就是把你的输入 JSON 映射成这套图层树再交给 Sketch 渲染引擎去绘制。很多人在做 json-sketchapp 二次开发时只盯着「如何生成一个矩形」其实关键在理解层级关系。比如一个画板artboard在 Sketch 内部是 class 为 artboard 的图层按钮则是 class 为 oval 或 rectangle 的形状图层叠上文字图层的组合。坐标永远相对父图层计算这意味着你写 JSON 时同样要遵守这个相对坐标系否则图层会跑到预期之外的角落。2.3 两种 JSON 输入别搞混SketchJSON 与更上层的描述语言这是新手最容易栽跟头的地方。json-sketchapp 这类插件通常接受两种输入。第一种是你手写的、贴近业务语义的高层 JSON比如「一个标题、一张图、一段说明文字间距多少、字体多大」这种 JSON 阅读成本低但它不是 Sketch 原生结构插件需要做一层翻译。第二种是 Sketch 原生页面 JSON也就是前面解压出来的那种里面全是 class、frame、style 这种底层字段修改它几乎等于直接在改设计稿。常见做法是写高层 JSON 再转换因为可读性好、易维护。我一般会先定义一套自己的简写规则例如type: title表示文本、type: image表示图片再在转换器里把它们展开成 Sketch 原生 JSON。这样做的代价是你需要维护映射关系但收益是项目里的设计资产变成了纯数据谁都能审、谁都能改。3. 把 JSON 变成 Sketch 文件安装插件与最小可用流程理解了 .sketch 的文件结构接下来的问题就是怎么在不双击打开 Sketch 的情况下让 JSON 变成可以交付的 .sketch 文件。这里有两套路径一套是走插件菜单交互适合偶尔转一次另一套是走命令行构建适合把 json 转换接进自动化流水线。我把两条路都走通一遍给你一个最小可用流程。3.1 安装插件与依赖准备先说说安装这件事。Sketch 插件本质是一个.sketchplugin包里面包含 manifest.json、脚本文件和资源。使用 skpm 脚手架构建是社区里最主流的做法npm install -g skpm skpm create json-sketchapp cd json-sketchapp npm install skpm build逻辑说明skpm 是 Sketch 官方社区常用的插件构建工具它帮你把 JavaScript 源码打包成 Sketch 能加载的插件结构。skpm build会在当前目录生成json-sketchapp.sketchplugin双击这个文件Sketch 就会把它安装到插件目录里。参数说明如果你只是用现成的 json-sketchapp 插件不需要自己构建直接从发布渠道下载编译好的 .sketchplugin 即可。但如果你要改映射规则、加自己的模板就必须从源码构建否则没法调试。这里需要注意Sketch 对插件的签名有要求自己构建的插件在别的机器上可能被 Sketch 拦截需要在系统设置里允许加载未签名插件。装完插件后在 Sketch 菜单栏能看到Plugins json-sketchapp这一项通常提供一个Import JSON的入口。手动使用时选中一个 JSON 文件插件读取后会在当前文档里新建页面并把图层树铺开。3.2 准备一份最小 JSON 并跑通转换手动点菜单适合验证但效率太低。我的建议是用命令行方式跑转换。很多 json-sketchapp 类插件会暴露 sketchtool 的run接口可以像下面这样批量调用sketchtool run ./json-sketchapp.sketchplugin \ --commandimport-json \ --input./design.json \ --output./output.sketch逻辑说明sketchtool是 Sketch 自带的一个命令行工具位于 Sketch 应用包的 Contents/Resources 目录里。通过run命令它可以在不打开 Sketch 图形界面的情况下启动插件执行命令。--input指定输入的 JSON 文件路径--output指定要生成的 .sketch 文件路径具体参数名称依赖插件实现但整体套路固定。参数说明--command的值对应 manifest.json 里注册的 command 标识符不是随便起的名字。你要先查看插件里的 manifest.json找到那个负责导入 JSON 的命令名称再传给 sketchtool。如果命令行跑不通最常见的原因就是插件压根没有注册 sketchtool 可调用的命令这时候退回手动菜单验证。这里我给一个更可复现的方案跳过插件 API直接用 Node.js 生成 .sketch 文件。既然 .sketch 是 zip那我们就手动组装里面的 JSON 再压缩。这种方式没有图形界面依赖进 CI 也不会因为 Sketch 授权而失败。const fs require(fs); const archiver require(archiver); function buildSketchFile(pagesJson, outputPath) { return new Promise((resolve, reject) { const output fs.createWriteStream(outputPath); const archive archiver(zip, { zlib: { level: 9 } }); output.on(close, resolve); archive.on(error, reject); archive.pipe(output); // 固定骨架document.json 是页面索引pages 目录是页面内容 archive.append( JSON.stringify({ _class: document, pages: pagesJson.map((page) ({ _class: page, id: page.id, name: page.name, })), }), { name: document.json } ); pagesJson.forEach((page) { archive.append(JSON.stringify(page), { name: pages/${page.id}.json }); }); archive.append( JSON.stringify({ appVersion: 98, build: 1, version: 1, }), { name: meta.json } ); archive.finalize(); }); }逻辑说明这段代码把页面 JSON 数组按 Sketch 的目录结构写入一个 zip 包。document.json 是索引declares 了每个页面的 id 和名字pages 目录放真正的页面内容meta.json 是版本信息。archiver负责压缩压缩级别设成 9 是为了让产物尽量小。参数说明id必须是 UUID 格式Sketch 打开文件时会按 id 关联页面索引与页面内容不一致会直接报错或白屏。version指文件格式版本不同 Sketch 版本要求的数值不一样一般写 1 兼容性最好。如果你不想引入 archiver也可以直接用命令行zip -r output.sketch document.json meta.json pages/完成压缩效果一样。3.3 从命令行批量转换把 json 转换过程接入 CI当量上来之后手动执行命令也不够得把它变成构建流程的一步。常见做法是写一个 Node.js 脚本作为转换入口输入一个目录下所有的 JSON输出对应的 .sketch 文件npx ts-node scripts/json-to-sketch.ts \ --input ./designs/*.json \ --output ./dist/const fs require(fs); const path require(path); const { buildSketchFile } require(./sketch-builder); const inputGlob process.argv[2]; const outputDir process.argv[3]; // 假设 inputGlob 是文件名模式这里是简单目录遍历 const files fs.readdirSync(inputGlob).filter((f) f.endsWith(.json)); for (const file of files) { const pages JSON.parse(fs.readFileSync(path.join(inputGlob, file), utf-8)); const outFile path.join(outputDir, file.replace(.json, .sketch)); buildSketchFile(pages, outFile).then(() { console.log(generated: ${outFile}); }); }逻辑说明这个批量转换脚本读取每个 JSON把它解析成页面数组然后调用buildSketchFile生成对应的 .sketch 文件。接入 CI 后只要设计侧提交一份 JSON流水线就会自动产出设计稿文件整个过程无需打开 Sketch。参数说明输入 JSON 的格式要和buildSketchFile期望的页面数组结构对齐这是最容易断掉的地方。建议在脚本开头加一个简单的 schema 校验例如检查_class字段是否存在、frame是否为对象。CI 上跑的时候要注意 Node 版本archiver 对 Node 版本有要求最好用项目里 lock 住的版本不然换个环境就会出现莫名的压缩符号表错误。4. 生成真实页面图层、文本、图片与布局映射框架跑通之后真正的活儿在内容映射怎么把你手里的产品信息变成 Sketch 能识别的 shape、text、image。这一章给出我在实际项目中稳定的映射方案以及这些参数背后的取舍。4.1 页面pages、画板artboards与组的映射关系先约定一种高层 JSON 输入格式。我一般把每个画板描述成一个对象里面有 type、frame、name 和 children{ type: page, name: 用户详情页, artboards: [ { type: artboard, name: 基础信息, frame: { x: 0, y: 0, width: 375, height: 812 }, children: [ { type: group, name: 头部信息, frame: { x: 0, y: 0, width: 375, height: 120 }, children: [] } ] } ] }转换成 Sketch 原生 JSON 时规则是这样的type 为 page 的对象映射成 pages 目录下的一个 JSON 文件type 为 artboard 的映射成 class 为 artboard 的图层type 为 group 映射成 class 为 group 的图层组。每一层都带frame和name其中 frame 的坐标是相对父图层计算的这点和 Sketch 内部一致。参数说明这是一个嵌套递归结构越深层级越多生成的文件越大。如果你的页面层级超过十层先检查是不是数据结构本身设计得过于复杂而不是转换器的问题。我见过有人在 JSON 里把每个文本都包一层 group导致产物膨胀且难以维护实际写平层反而更稳。4.2 文本与图片怎么在 JSON 里声明文本是设计稿里最重要也最容易出错的元素。一个稳定的文本映射如下{ type: text, name: 用户姓名, frame: { x: 16, y: 24, width: 200, height: 22 }, text: 张三, style: { fontSize: 16, fontFamily: PingFang SC, fontWeight: 600, color: #333333, alignment: left } }转换成 Sketch 的 attributedString 时需要把 style 展开成一段带属性的富文本。Sketch 内部用attributedString.attributes数组承载字体、颜色、字距这些信息多层嵌套很容易写错。我一般会写一个makeAttributedString(text, style)函数来统一处理把字体名转成 Sketch 的字体描述结构把 hex 颜色拆成 RGBA 分量。图片的声明稍微简单常见做法是用 base64 内嵌{ type: image, name: 头像, frame: { x: 16, y: 64, width: 64, height: 64 }, base64: iVBORw0KGgoAAAANSUhEUgAA... }转换时把 base64 写成图层的image属性Sketch 会在打开文件时把数据渲染出来。参数说明base64 会让 JSON 体积暴增尽量不要内嵌超过 1MB 的图片不然插件处理会变慢更好的做法是 JSON 里存相对路径转换时由脚本读取文件再写入 sketch 包内的 assets 目录。4.3 布局约束与响应式参数怎么设Sketch 的布局系统里有 pins夹边、resizingConstraint缩放约束这些参数。json-sketchapp 这类工具最常见的短板就是生成的画板在改变尺寸时里面的元素不会自适应因为 JSON 里没有写约束规则。我一般会在高层 JSON 里声明layout字段让转换器生成对应的约束{ type: text, name: 标题, frame: { x: 16, y: 24, width: 200, height: 22 }, layout: { pinLeft: true, pinRight: true, pinTop: true, fixedHeight: true } }转换器把这个 layout 转成 Sketch 的resizingConstraint整型值。这个值是按位计算的固定左边为 1固定右边为 2固定顶部为 4固定底部为 8组合起来就是二进制位相加。例如pinLeft pinRight pinTop得到 7表示左右顶都固定。写约束的时候注意约定的fixedHeight和pinTop组合起来才是「高度固定、位置随顶部走」很多人只写了fixedHeight忘了pinTop结果改画板高度时元素直接跟着顶边跑了。参数说明这个位掩码的计算极易出错建议定义成常量表并提供单元测试。测试样例就是「一个 375 宽的画板里有左右间距 16 的按钮画板拉宽到 414 后按钮右边缘仍在距右边 16 处」这是验证 resizingConstraint 是否正确的最快方式。5. 避坑指南sketch 插件跑转换常见的 5 个翻车现场工具能跑通和能稳定产出之间隔着一堆小问题。下面的五个坑都是我在实际项目里遇到过的按「现象 → 原因 → 解决」的节奏写每一段都来自真实排错经历。5.1 转换出的文件打不开或者 Sketch 打开后是一片空白现象插件跑完提示成功.sketch 文件也生成了但双击打开 Sketch 直接弹「无法打开文档」或者能打开但画布上什么都没有。原因Sketch 打开文件时会做严格的结构校验最常见的问题是 pages 目录里的 JSON 里页面 id 和 document.json 里声明的不一致或者是 meta.json 里缺少必要的 version 字段。另一个被我踩过的是用底层方案直接拼 zip 时压缩包目录顺序不对Sketch 会认为文件损坏。解决先用unzip -t output.sketch检查压缩包是否完整再把文件改名成 .zip 解压手动比对 document.json 里的 pages 数组与 pages 目录下的文件名是否一一对应。我最后给脚本加了一步验证生成后自动解包并检查 id 一致性不一致就报错退出从根上杜绝这个坑。5.2 中文文本乱码或字体被替换现象转换后的设计稿里中文内容显示成方块或者默认英文字体特别是打包到别的机器上打开时尤其明显。原因转换器里写的 fontFamily 名称和 Sketch 字体系统的命名不一致。Sketch 内部使用 PostScript 字体名比如中文经常是PingFangSC-Semibold而不是直接写PingFang SC。此外如果文本里没有声明字体子类Sketch 会退回去找系统默认字体中文环境下这个默认字体往往不是你以为的那个。解决在转换器里做一个字体名映射表把「中文简体、加粗」这种业务描述映射成 PostScript 名没有映射到的字体干脆不写让 Sketch 走自己的回退逻辑。同时把attributedString里的NSFont字段补齐这里漏掉任何一项都会导致字体被替换。验证方法是生成后用打开文件用字体面板看每个文本层的字体归属。5.3 图层全堆在左上角坐标完全对不上现象生成的画板里每个元素都挤在 (0,0) 附近明明 JSON 里写的 x、y 是 16、24。原因很多 json-sketchapp 转换器在映射图层时会忽略背景画板的坐标把子图层坐标直接当成绝对坐标写进页面但 Sketch 期望的是相对父画板的坐标。如果画板本身在页面里位于 (0,0)那子图层写 (16,24) 没问题当画板移动到 (100,200) 时子图层还是写 (16,24) 就会错位。另一个原因是父 group 的坐标被重置为零导致所有子元素相对于画板左上角堆叠。解决写一个坐标规整函数递归遍历图层树保证每个 layer 的 frame 坐标是相对其直接父层的。路径就是把整个设计稿的根画板当成原点子元素只维护相对位移生成时再把画板在页面里的绝对位置加回去。这个函数一定要用真实页面数据做测试只在单一画板上验证过很容易漏。5.4 Symbol 实例没有替换生成一堆零散图层现象JSON 里声明了一个按钮组件转换后的 Sketch 里它是一堆散落的矩形和文本而不是一个可复用的 Symbol。原因Sketch 的 Symbol 机制依赖库library和 symbol 实例 id。json-sketchapp 这类工具通常没有内置 Symbol 库的符号表或者插件在转换时只按图层树逐层生成没做「这个 group 应该关联到某个 symbol master」的映射。解决转换前先从 Symbols 页面提取所有 symbol 实例的 id 和名称在 JSON 里用symbolName字段声明引用关系。转换器看到symbolName时不递归生成子图层而是在该位置放一个 class 为 symbolInstance 的图层并在symbolID里填上对应的 master id。这个做法需要维护一份 symbol 清单但收益是可复用组件统一由设计库控制改一处全稿更新。5.5 sketchtool 在 CI 上跑不起来报各种路径错误现象本地明明能跑通 sketchtool run一到 CI 的 macOS 机器上就报「无法加载插件」或「Sketch 未运行」。原因sketchtool 依赖 Sketch.app 的完整安装CI 机器上如果只装了命令行工具没装完整 Sketch或者 Sketch 版本与插件最低要求不符就会出问题。更隐蔽的原因是 CI 环境里 Sketch 没有窗口会话权限命令行启动插件时插件里如果有代码调用了需要 UI 线程的 API就会挂起。解决如果只是要生成 .sketch 文件用我前面提的 Node.js 旁路方案别依赖 sketchtool。如果必须用插件能力那就把 CI 构建机固定成带标准 Sketch 安装的机器并在启动命令前加一个open -a Sketch保活步骤。调run命令时加上--verbose看日志大部分报错信息足够定位到插件源码的具体行。6. 验证输出与进阶玩法把 Sketch 文件当数据看到这里你已经能稳定产出 .sketch 文件了但交付前还需要验证以及考虑怎么让这套能力产生更大的价值。6.1 验证转换结果的三步法第一步脚本验证解包产出的 .sketch用jq检查 document.json 里的 pages 数量、每个页面的图层数量以及所有图层 frame 是否在画板范围内。第二步渲染验证用 sketchtool 导出画板为 PNG代码逻辑生成的预览图要在画布上对得起来。sketchtool export Artboard ./output.sketch --output./preview/第三步人眼抽检随机选 5% 的画板打开 Sketch 对比导出图和预期设计稿。前两步能抓住绝大多数结构问题第三步是因为某些字体渲染细节只有人眼能判断比如行高差异导致文本溢出。6.2 进阶一模板化生成设计资产一旦 JSON 驱动生成这条链路稳定整个设计交付方式就会改变。我在团队里做了模板化方案定义一套组件模板把每个页面的数据抽成纯 JSON业务侧改数据脚本统一重新生成 .sketch 文件。这样一次接口字段变化不再需要设计师重出一版图而是脚本跑完自动出新稿。实现方式就是在转换器里维护一个模板映射表把「页面类型 场景」组合映射到固定的图层树模板模板里只留几个插值变量。6.3 进阶二反向校验把 .sketch 里的 JSON 抽出来比对更严谨的做法是建立起正向和反向的闭环。我写过一个反向脚本把 Sketch 文件解包从页面 JSON 里抽出所有文本、图片、坐标和样式再反序列化成我最初的高层 JSON然后和输入 JSON 做 diff。这能发现样式字段在转换过程中被悄悄吞掉的问题比如字体粗细精度丢失、颜色写成小数。这个过程的成本不高但收益巨大它让每一个字段的变化都有迹可查。用完后我会把校验脚本放进 CI日常开发里很少再出现「设计稿和 JSON 对不上」的玄学问题。做成这件事之后我自己的习惯是凡是超过五个同构页面一律先写 JSON 再谈画布。毕竟设计稿说到底就是一叠数据把 json 转换这条路径夯实了后面无论是批量换肤、多语言导出还是跟前端组件库做一次数据对齐都变成同一套管道里的顺畅活。希望这套思路也能帮到正在被重复画布折磨的你。本文还有配套的精品资源点击获取
返回列表