攜程機(jī)票Sketch插件開發(fā)實踐
時間:2023-05-28 06:15:01 | 來源:網(wǎng)站運(yùn)營
時間:2023-05-28 06:15:01 來源:網(wǎng)站運(yùn)營
攜程機(jī)票Sketch插件開發(fā)實踐:Sketch 是伴隨移動應(yīng)用程序崛起而流行的 UI 設(shè)計工具。2014年 Sketch V3 增加 Symbols 功能,在 UI 設(shè)計工具領(lǐng)域的優(yōu)勢越來越大。它持續(xù)改進(jìn)和增強(qiáng)功能,不斷加強(qiáng)對插件社區(qū)的建設(shè),吸引越來越多的開發(fā)者進(jìn)入。
隨著 Design System 的普及和流行,許多大公司都在設(shè)計插件領(lǐng)域有所投入,如 Google,Airbnb 等,同時誕生了一系列提供設(shè)計管理的初創(chuàng)企業(yè),如 Abstract,UXPIN 等。中國很多大中型互聯(lián)網(wǎng)企業(yè)也開始研制自己的設(shè)計系統(tǒng)和插件工具,例如 Dapllo,Kitchen,F(xiàn)usion,Anto。
2016年,攜程機(jī)票UED團(tuán)隊主力生產(chǎn)工具完全切換到 Sketch。與此同時,機(jī)票前端研發(fā)技術(shù)團(tuán)隊也關(guān)注到設(shè)計系統(tǒng)和插件工具規(guī)范化自動化對業(yè)務(wù)交付工作流程的加速作用。于是設(shè)計團(tuán)隊與前端技術(shù)團(tuán)隊開始攜手探索設(shè)計語言升級,從業(yè)務(wù)需求項目實踐中提煉通用規(guī)范,落地插件工具系統(tǒng),并輸出一整套設(shè)計資源管理規(guī)范和流程。
一、設(shè)計資源管理
產(chǎn)品的設(shè)計語言是一家公司留給消費者最直觀的形象,通常包含品牌 Logo,顏色,文字,符號,插畫,動畫和文案話術(shù)等。而 Design System 是為了讓設(shè)計語言落地執(zhí)行而構(gòu)建的解決方案,包括設(shè)計指導(dǎo)文檔,設(shè)計資源,組件代碼和工具等。
設(shè)計資源的共享和版本管理一直困擾著設(shè)計師們。Sketch 通過引入 Symbols,Library 等功能解決了共享組件問題,但在顏色,文字排版,圖標(biāo)管理等問題上依然缺少足夠好的解決方案,需要各家公司針對自身的特點開發(fā)插件和服務(wù)去解決這些問題。
二、Kirby - Sketch Plugin
Kirby 是攜程機(jī)票前端團(tuán)隊的 Sketch Plugin 系統(tǒng)代號,故事源于一次 Sketch 版本升級引起的標(biāo)注導(dǎo)出功能失效問題。
Sketch 社區(qū)著名插件 Sketch Measure,它將設(shè)計稿和數(shù)據(jù)參數(shù)導(dǎo)出 HTML 網(wǎng)頁,供技術(shù)人員查看,節(jié)省從設(shè)計到開發(fā)過程的溝通成本。但 Sketch 的版本升級經(jīng)常致其無法正常使用,維護(hù)者也無暇快速修復(fù)問題,設(shè)計師們?nèi)衾^續(xù)使用,將被迫使用一個低版本 Sketch。
為了讓設(shè)計師能夠使用 Sketch 最新版本,并修復(fù) Sketch Measure 缺陷功能,Kirby 誕生。
之后我們陸續(xù)添加更多功能,從 Design Token,到 Icon System,再到 Component,以及 Template,一步步接近成熟和完整。
Kirby 目前已交付若干重要特性:
2.1設(shè)計語言規(guī)范約束Color Pallete
Typography
Shadow
2.2 設(shè)計稿靜態(tài)掃描檢查方便設(shè)計師查找和修正問題。
字體與圖標(biāo)系統(tǒng)
在線版設(shè)計資源模版庫
三、Sketch Plugin 開發(fā)技術(shù)
在插件開發(fā)實現(xiàn)過程中,我們遇到許多交叉技術(shù)領(lǐng)域的問題,也因此進(jìn)行了多次技術(shù)重構(gòu)。
3.1 基礎(chǔ)知識Sketch 官方技術(shù)文檔提供了簡單介紹:
1)Sketch 插件是按照特定方式管理的一個文件夾,包含一個或多個 scripts,每個 script 含有若干擴(kuò)展 Sketch 用途的命令。
2)插件主要使用 Javascript 語言編寫,支持 ES6 語法,但運(yùn)行環(huán)境既不是瀏覽器也不是 Nodejs,而是 Hybrid SketchAPI for macOS Native 運(yùn)行環(huán)境。
從最簡單的部分看起:
打開一個 Sketch 文件,control + shift + k 快捷鍵開啟 Run Script 面板,輸入:
const sketch = require('sketch')sketch.UI.message('Hello, world!')
運(yùn)行以上代碼,將在 Sketch 文件下方區(qū)域顯示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 插件系統(tǒng)開放了幾乎所有權(quán)限,讓許多天馬行空的想法可以實現(xiàn)。但是插件開發(fā)者需要及時關(guān)注 Sketch 版本升級,其向下兼容性較差,或者說官方團(tuán)隊并不重視這部分。典型案例例如,著名插件 Paddy,在開發(fā)V2.0版本過程中,因 Sktech API 大量變更,原插件無法兼容,作者最終無力修復(fù)而放棄開發(fā),非??上?。
官方 API 有兩個: Actions API, Javascript API。
3.3 Actions API用于監(jiān)聽用戶操作行為和觸發(fā)的事件。據(jù)社區(qū)消息,該API未來會被新的 Events API 替代。它代表了 Sketch App 內(nèi)部觸發(fā)的事件,例如 CloseDocument, TogglePresentationMode 等, 細(xì)節(jié)詳見官網(wǎng):
https://developer.sketch.com/reference/action/1)訂閱 Actions
manifest.json 文件,配置相應(yīng) handlers。
示例:當(dāng) OpenDocument 事件被觸發(fā)時調(diào)用 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 包含兩個狀態(tài),begin 和 finished,例如 SelectionChanged 。需分別訂閱 SelectionChanged.begin 和 SelectionChanged.finished,否則會觸發(fā)兩次事件。
當(dāng)我們不知道應(yīng)該訂閱哪個 Action 時,可以使用通配符。但運(yùn)行時性能開銷很大,建議僅在開發(fā)階段使用。
示例:Terminal 打開通配符支持。
defaults write com.bohemiancoding.sketch3 actionWildcardsAllowed -bool YES
manifest.json 文件配置通配符。
{ ... "handlers": { "actions": { "*": "onActionHandler" } } ...}
3.4 Javascript API它是針對 Native API 的封裝,目前還未涵蓋所有場景,官方承諾未來將覆蓋90%。細(xì)節(jié)詳見官網(wǎng)和GitHub:
https://developer.sketch.com/reference/api/https://github.com/BohemianCoding/SketchAPI/blob/develop/CHANGELOG.json3.5 開發(fā)環(huán)境官方提供了一個打包工具 skpm, 用于快速上手插件開發(fā)。它基于 webpack,項目根目錄下存放 webpack.skpm.config.js, 用于工程配置修改。
安裝示例
npm install -g skpmskpm create my-plugincd my-pluginnpm run build# 然后在 Plugins -> my-plugin -> MyCommand 中運(yùn)行插件命令
典型的插件開發(fā)工程的目錄結(jié)構(gòu)示例
├── .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 崩潰保護(hù)當(dāng) Sketch 運(yùn)行發(fā)生崩潰,它會停用所有插件以避免循環(huán)崩潰。對于使用者,每次崩潰重啟后手動在菜單欄啟用所需插件非常繁瑣。因此可以通過如下命令禁用該特性。
defaults write com.bohemiancoding.sketch3 disableAutomaticSafeMode true
3.7 插件緩存通過配置啟用或禁用緩存機(jī)制:
defaults write com.bohemiancoding.sketch3 AlwaysReloadScript -bool YES
但是該方法對于某些場景不適用。例如當(dāng)正在使用一個 long-running 腳本時,即 Javascript Context 不變,并存儲在內(nèi)存中,那么則需要重啟 Sketch 或通過 coscript.setShouldKeepAround(false) 使改動生效。
3.8 WebView 調(diào)試如果插件實現(xiàn)方案使用 WebView 做界面,可通過以下配置開啟調(diào)試功能。
defaults write com.bohemiancoding.sketch3 WebKitDeveloperExtras -bool true
3.9 打印輸出日志Sketch 運(yùn)行環(huán)境 JavascriptCore 的日志輸出方式:
1)macOS console.app 中搜索 與 sketch 相關(guān)的 Filter。
2)查看 ~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log。
3)運(yùn)行 skpm log 查看上述文件,-f 參數(shù)使用 stream 方式查看。
4)使用 skpm 開發(fā)的插件,可以使用 console.log 語法,需安裝 sketch-dev-tools。
3.10 代碼調(diào)試Safari 瀏覽器開發(fā)工具可用于插件的 Javascript 代碼調(diào)試。
Developer > name of your machine > Automatically Show Web Inspector for JSContexts
同時啟用選項 Automatically Pause Connecting to JSContext
3.11 Objective-C ClassesSketch 對外暴露了全部內(nèi)部方法的調(diào)用權(quán)限,其 Objective-C 類 通過 Bridge 提供 Javascript API 調(diào)用。
3.12 插件 GUI 的實現(xiàn)用戶(設(shè)計師)可以通過以下幾種方式使用插件:
1)菜單 -> 插件 -> 選擇插件已定義的 Handler
2)Handler 預(yù)定義的快捷鍵
3)插件提供的其他 GUI 方案
其他 GUI 方案有 Native(Appkit) 和 WebView ,以 Kirby 為例,目前大部分 UI 由 WebView 實現(xiàn)。這種方案對于 Web 前端技術(shù)人員上手學(xué)習(xí)成本較低。 skpm 提供了已封裝好的基于 WKWebView 的 sketch-module-web-view,其 API 設(shè)計接近 Electron,細(xì)節(jié)詳見 GitHub。
https://github.com/skpm/sketch-module-web-view/blob/master/docs/browser-window.md除此之外,skpm 還提供了一些供 JavaScript 調(diào)用的 Native 控件,例如 Dialog:
import dialog from '@skpm/dialog'console.log( dialog.showOpenDialog({ properties: ['openFile', 'openDirectory', 'multiSelections'] }))
3.13 CocoaScript GUICocoaScript 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 調(diào)用 Objective-C 方法,需要將方法名的冒號改為下劃線(最后一個可選),所有 selector 連接成一個連續(xù)的字符串。例如 executeOperation:withObject:error: 將轉(zhuǎn)為 executeOperation_withObject_error()。
示例:
/*打開文件對話框,使用 Appkit NSOpenPanel。AppKit | Apple Developer Documentation*/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()//設(shè)置異步操作不被 Sketch GC回收COScript.currentCOScript().shouldKeepAround = true//異步執(zhí)行完成后,釋放COScript.currentCOScript().shouldKeepAround = false
3.14 Native GUI實現(xiàn) Native GUI 的難點在于“合適的時機(jī)”找到“正確的實例”。Actions API 用于解決“合適的時機(jī)”問題。而尋找“正確的實例”相對復(fù)雜一些。首先從 Sketch-Headers 入手,搜索與事件有關(guān)的方法。
例如,當(dāng)我們需要擴(kuò)展 “創(chuàng)建組件“ 對話框,該對話框在 “創(chuàng)建組件” 事件發(fā)生時才會出現(xiàn),因此在 Actions API 中搜索與 symbol 有關(guān)的事件,找到名為 Create Symbol 的 Action。然后繼續(xù)搜索關(guān)鍵字 CreateSymbol,可找到如下結(jié)果。
code 能夠以黑盒方式分析系統(tǒng)當(dāng)前運(yùn)行的應(yīng)用程序界面,提取對于插件開發(fā)有用的信息。使用 Xcode 分析 Sketch 可知,MSCreateSymbolNamingSheet 繼承自 NSWindowController,如下示例代碼可獲取其調(diào)用對象。
const doc = context.actionContext.document;const docData = doc.documentData();const window = doc.window();const sheetWindow = window.attachedSheet();const createSymbolNameingSheet = sheetWindow.windowController();
3.15 Webview GUIWebview 與 Plugin 之間需要實現(xiàn)雙向通訊互操作。Webview 可通過 WKWebView delegates 向 Plugin 發(fā)送消息,Plugin 則通過webView.evaluateJavaScript_completionHandler() 觸發(fā) Webview 執(zhí)行 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插件系統(tǒng),除了上述技術(shù)實現(xiàn)細(xì)節(jié),還有很多需要思考兼顧的方面。例如,設(shè)計文件版本管理,Sketch 版本兼容性,用戶配置文件,云同步協(xié)作,用戶(企業(yè)內(nèi)網(wǎng))鑒權(quán)認(rèn)證接入。
Kirby 每一個新增功能都是圍繞著從設(shè)計到研發(fā)再到交付的一致性和復(fù)用性,這也正是設(shè)計系統(tǒng)所要解決的核心問題。
我們 hack 的不僅是一個軟件,而是完整的 DesignOps 工作流程。【作者簡介】尹正波,攜程機(jī)票研發(fā)部前端工程師,專注設(shè)計和開發(fā)的交叉領(lǐng)域,用系統(tǒng)和工具改進(jìn)設(shè)計體驗和交付。
更多攜程技術(shù)人一手干貨,歡迎搜索關(guān)注“攜程技術(shù)中心”微信公眾號~