干货 | 携程机票Sketch插件开发实践

作者简介

尹正波,携程机票研发部前端工程师,专注设计和开发的交叉领域,用系统和工具改进设计体验和交付。


Sketch 是伴随移动应用程序崛起而流行的 UI 设计工具。2014年 Sketch V3 增加 Symbols 功能,在 UI 设计工具领域的优势越来越大。它持续改进和增强功能,不断加强对插件社区的建设,吸引越来越多的开发者进入。


640?wx_fmt=png

640?wx_fmt=png


随着 Design System 的普及和流行,许多大公司都在设计插件领域有所投入,如 Google,Airbnb 等,同时诞生了一系列提供设计管理的初创企业,如 Abstract,UXPIN 等。中国很多大中型互联网企业也开始研制自己的设计系统和插件工具,例如 Dapllo,Kitchen,Fusion,Anto。


2016年,携程机票UED团队主力生产工具完全切换到 Sketch。与此同时,机票前端研发技术团队也关注到设计系统和插件工具规范化自动化对业务交付工作流程的加速作用。于是设计团队与前端技术团队开始携手探索设计语言升级,从业务需求项目实践中提炼通用规范,落地插件工具系统,并输出一整套设计资源管理规范和流程。


一、设计资源管理


产品的设计语言是一家公司留给消费者最直观的形象,通常包含品牌 Logo,颜色,文字,符号,插画,动画和文案话术等。而 Design System 是为了让设计语言落地执行而构建的解决方案,包括设计指导文档,设计资源,组件代码和工具等。


设计资源的共享和版本管理一直困扰着设计师们。Sketch 通过引入 Symbols,Library 等功能解决了共享组件问题,但在颜色,文字排版,图标管理等问题上依然缺少足够好的解决方案,需要各家公司针对自身的特点开发插件和服务去解决这些问题。


640?wx_fmt=png


二、Kirby - Sketch Plugin


Kirby 是携程机票前端团队的 Sketch Plugin 系统代号,故事源于一次 Sketch 版本升级引起的标注导出功能失效问题。


Sketch 社区著名插件 Sketch Measure,它将设计稿和数据参数导出 HTML 网页,供技术人员查看,节省从设计到开发过程的沟通成本。但 Sketch 的版本升级经常致其无法正常使用,维护者也无暇快速修复问题,设计师们若继续使用,将被迫使用一个低版本 Sketch。


为了让设计师能够使用 Sketch 最新版本,并修复 Sketch Measure 缺陷功能,Kirby 诞生。


之后我们陆续添加更多功能,从 Design Token,到 Icon System,再到 Component,以及 Template,一步步接近成熟和完整。


Kirby 目前已交付若干重要特性:


2.1设计语言规范约束


Color Pallete


640?wx_fmt=png


Typography


640?wx_fmt=png


Shadow


640?wx_fmt=png


2.2 设计稿静态扫描检查


方便设计师查找和修正问题。


640?wx_fmt=png


字体与图标系统


640?wx_fmt=png


在线版设计资源模版库


640?wx_fmt=png


三、Sketch Plugin 开发技术


在插件开发实现过程中,我们遇到许多交叉技术领域的问题,也因此进行了多次技术重构。


3.1 基础知识


Sketch 官方技术文档提供了简单介绍:


1)Sketch 插件是按照特定方式管理的一个文件夹,包含一个或多个 scripts,每个 script 含有若干扩展 Sketch 用途的命令。


2)插件主要使用 Javascript 语言编写,支持 ES6 语法,但运行环境既不是浏览器也不是 Nodejs,而是 Hybrid SketchAPI for macOS Native 运行环境。


从最简单的部分看起:


打开一个 Sketch 文件,control + shift + k 快捷键开启 Run Script 面板,输入:


 
 
const sketch = require('sketch')	
sketch.UI.message('Hello, world!')


运行以上代码,将在 Sketch 文件下方区域显示Toast:Hello, world,该 Script 面板常用于快速测试脚本API。


3.2 API 概述


The plugin system in Sketch gives you full access to the app’s internals and the core frameworks in macOS. So you have an immense power to build almost anything.


Sketch 插件系统开放了几乎所有权限,让许多天马行空的想法可以实现。但是插件开发者需要及时关注 Sketch 版本升级,其向下兼容性较差,或者说官方团队并不重视这部分。典型案例例如,著名插件 Paddy,在开发V2.0版本过程中,因 Sktech API 大量变更,原插件无法兼容,作者最终无力修复而放弃开发,非常可惜。


官方 API 有两个: Actions API, Javascript API。


3.3 Actions API


用于监听用户操作行为和触发的事件。据社区消息,该API未来会被新的 Events API 替代。它代表了 Sketch App 内部触发的事件,例如 CloseDocument, TogglePresentationMode 等, 细节详见官网:

https://developer.sketch.com/reference/action/


1)订阅 Actions


manifest.json 文件,配置相应 handlers。


示例:当 OpenDocument 事件被触发时调用 onOpenDocument handler 。


 
 
"commands" : [	
  ...	
  {	
    "script" : "my-action-listener.js",	
    "name" : "My Action Listener",	
    "handlers" : {	
      "actions": {	
        "OpenDocument": "onOpenDocument"	
      }	
    },	
    "identifier" : "my-action-listener-identifier"	
  }	
  ...	
],


my-action-listener.js


 
 
export function onOpenDocument(context) {	
  context.actionContext.document.showMessage('Document Opened')	
}


2)Action Context


Action事件会将 context.actionContext 传递给handler。有些 Action 包含两个状态,begin 和 finished,例如 SelectionChanged 。需分别订阅 SelectionChanged.begin 和 SelectionChanged.finished,否则会触发两次事件。


当我们不知道应该订阅哪个 Action 时,可以使用通配符。但运行时性能开销很大,建议仅在开发阶段使用。


示例:Terminal 打开通配符支持。


 
 
defaults write com.bohemiancoding.sketch3 actionWildcardsAllowed -bool YES


manifest.json 文件配置通配符。


 
 
{	
  ...	
  "handlers": {	
    "actions": {	
      "*": "onActionHandler"	
    }	
  }	
  ...	
}


3.4 Javascript API


它是针对 Native API 的封装,目前还未涵盖所有场景,官方承诺未来将覆盖90%。细节详见官网和GitHub:

https://developer.sketch.com/reference/api/

https://github.com/BohemianCoding/SketchAPI/blob/develop/CHANGELOG.json


640?wx_fmt=png


3.5 开发环境


官方提供了一个打包工具 skpm, 用于快速上手插件开发。它基于 webpack,项目根目录下存放 webpack.skpm.config.js, 用于工程配置修改。


安装示例


 
 
npm install -g skpm	
skpm create my-plugin	
cd my-plugin	
npm run build	
# 然后在 Plugins -> my-plugin -> MyCommand 中运行插件命令


典型的插件开发工程的目录结构示例

.

 
 
├── .gitignore	
├── README.md	
├── src                         // sources	
│   ├── manifest.json           // plugin's manifest	
│   └── my-command.js           // source code of the command	
├── node_modules	
│   └── skpm                    // the sketch plugin developer toolchain	
├── my-plugin.sketchplugin      // compilation output, the actual plugin	
│   └── Contents	
│       ├── Resources	
│       └── Sketch	
│           ├── manifest.json	
│           └── my-command.js	
└── package.json


3.6 崩溃保护


当 Sketch 运行发生崩溃,它会停用所有插件以避免循环崩溃。对于使用者,每次崩溃重启后手动在菜单栏启用所需插件非常繁琐。因此可以通过如下命令禁用该特性。


 
 
defaults write com.bohemiancoding.sketch3 disableAutomaticSafeMode true


3.7 插件缓存


通过配置启用或禁用缓存机制:


 
 
defaults write com.bohemiancoding.sketch3 AlwaysReloadScript -bool YES


但是该方法对于某些场景不适用。例如当正在使用一个 long-running 脚本时,即 Javascript Context 不变,并存储在内存中,那么则需要重启 Sketch 或通过 coscript.setShouldKeepAround(false) 使改动生效。


3.8 WebView 调试


如果插件实现方案使用 WebView 做界面,可通过以下配置开启调试功能。


 
 
defaults write com.bohemiancoding.sketch3 WebKitDeveloperExtras -bool true


3.9 打印输出日志


Sketch 运行环境 JavascriptCore 的日志输出方式:


1)macOS console.app 中搜索 与 sketch 相关的 Filter。

2)查看 ~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log。

3)运行 skpm log 查看上述文件,-f 参数使用 stream 方式查看。

4)使用 skpm 开发的插件,可以使用 console.log 语法,需安装 sketch-dev-tools。


3.10 代码调试


Safari 浏览器开发工具可用于插件的 Javascript 代码调试。


Developer > name of your machine > Automatically Show Web Inspector for JSContexts

同时启用选项 Automatically Pause Connecting to JSContext


3.11 Objective-C Classes


Sketch 对外暴露了全部内部方法的调用权限,其 Objective-C 类 通过 Bridge 提供 Javascript API 调用。


3.12 插件 GUI 的实现


用户(设计师)可以通过以下几种方式使用插件:


1)菜单 -> 插件 -> 选择插件已定义的 Handler

2)Handler 预定义的快捷键

3)插件提供的其他 GUI 方案


其他 GUI 方案有 Native(Appkit) 和 WebView ,以 Kirby 为例,目前大部分 UI 由 WebView 实现。这种方案对于 Web 前端技术人员上手学习成本较低。 skpm 提供了已封装好的基于 WKWebView 的 sketch-module-web-view,其 API 设计接近 Electron,细节详见 GitHub。

https://github.com/skpm/sketch-module-web-view/blob/master/docs/browser-window.md


除此之外,skpm 还提供了一些供 JavaScript 调用的 Native 控件,例如 Dialog:


 
 
import dialog from '@skpm/dialog'	
console.log(	
  dialog.showOpenDialog({	
    properties: ['openFile', 'openDirectory', 'multiSelections']	
  })	
)


3.13 CocoaScript GUI


CocoaScript is built on top of Apple’s JavaScriptCore, the same JavaScript engine that powers Safari. So when you write in CocoaScript, you are really writing JavaScript.


CocoaScript also includes a bridge which lets you access Apple’s Cocoa frameworks from JavaScript. This means you have a ton wonderful classes and functions you can use in addition to the standard JavaScript library.


Sketch 插件通过 Mohca / CocoaScript 连接到 Objective-C / Cocoa。Javascript 调用 Objective-C 方法,需要将方法名的冒号改为下划线(最后一个可选),所有 selector 连接成一个连续的字符串。例如 executeOperation:withObject:error: 将转为 executeOperation_withObject_error()。


示例:


 
 
/*	
打开文件对话框,使用 Appkit NSOpenPanel。	
https://developer.apple.com/documentation/appkit/nsopenpanel?language=objc	
*/	
var openPanel = NSOpenPanel.openPanel()	
openPanel.setCanChooseDirectories(false)	
openPanel.setCanChooseFiles(true)	
openPanel.setCanCreateDirectories(false)	
openPanel.setDirectoryURL(NSURL.fileURLWithPath('~/Documents/'))	
openPanel.setTitle('Choose a file')	
openPanel.setPrompt('Choose')	
openPanel.runModal()	
	
	
//设置异步操作不被 Sketch GC回收	
COScript.currentCOScript().shouldKeepAround = true	
//异步执行完成后,释放	
COScript.currentCOScript().shouldKeepAround = false


3.14 Native GUI


实现 Native GUI 的难点在于“合适的时机”找到“正确的实例”。Actions API 用于解决“合适的时机”问题。而寻找“正确的实例”相对复杂一些。首先从 Sketch-Headers 入手,搜索与事件有关的方法。


例如,当我们需要扩展 “创建组件“ 对话框,该对话框在 “创建组件” 事件发生时才会出现,因此在 Actions API 中搜索与 symbol 有关的事件,找到名为 Create Symbol 的 Action。然后继续搜索关键字 CreateSymbol,可找到如下结果。


640?wx_fmt=png

code 能够以黑盒方式分析系统当前运行的应用程序界面,提取对于插件开发有用的信息。使用 Xcode 分析 Sketch 可知,MSCreateSymbolNamingSheet 继承自 NSWindowController,如下示例代码可获取其调用对象。


 
 
const doc = context.actionContext.document;	
const docData = doc.documentData();	
const window = doc.window();	
const sheetWindow = window.attachedSheet();	
const createSymbolNameingSheet = sheetWindow.windowController();


640?wx_fmt=png

3.15 Webview GUI


Webview 与 Plugin 之间需要实现双向通讯互操作。Webview 可通过 WKWebView delegates 向 Plugin 发送消息,Plugin 则通过webView.evaluateJavaScript_completionHandler() 触发 Webview 执行 Javascript 代码。


 
 
export function createPopoverWKHandler () {	
    return new MochaJSDelegate({	
        'userContentController:didReceiveScriptMessage:': (function (controller, message) {	
            let body = Utils.toJSON(message.body()),	
                { key, value } = body	
            switch (key) {	
                case 'setTypography':	
                    Utils.setTextStyle(value)	
                    break	
            }	
        })	
    })	
}


四、Milestone One


一个功能完整易用性佳的Sketch插件系统,除了上述技术实现细节,还有很多需要思考兼顾的方面。例如,设计文件版本管理,Sketch 版本兼容性,用户配置文件,云同步协作,用户(企业内网)鉴权认证接入。


Kirby 每一个新增功能都是围绕着从设计到研发再到交付的一致性和复用性,这也正是设计系统所要解决的核心问题。


我们 hack 的不仅是一个软件,而是完整的 DesignOps 工作流程。


【推荐阅读】



640?wx_fmt=jpeg


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值