日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

积木Sketch Plugin:设计同学的贴心搭档

發布時間:2023/12/20 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 积木Sketch Plugin:设计同学的贴心搭档 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

多年來,美團外賣一直在高速增長,但整個客戶端的UI組件一直沒有得到很好的統一。而在開發過程中因UI缺乏同一的標準導致各種問題凸顯,積木插件Sketch Plugin應運而生。外賣技術團隊將其打造成為UI一致性的抓手,最終幫助團隊減少開發成本,提升了交付的質量,并為美團多個業務團隊提供了很好的支持服務。

本文主要介紹了Sketch Plugin項目的背景,并由淺入深地帶領大家認識了Sketch Plugin項目,文中還詳細解讀了該技術的各種優缺點,同時還提供了詳細的實踐步驟和踩坑總結。希望本文對大家打造一致性體驗能夠有所幫助。

|?A?consistent experience is a better experience.——Mark Eberman

|?一致的體驗是更好的體驗。——Mark Eberman,知名設計師

背景

1. UI一致性項目

積木(Tangram)Sketch插件源于美團外賣UI的一致性項目,該項目自2019年5月份被提出,是UI設計團隊與研發團隊共建的項目,目的是改善用戶端體驗的一致性,提升多技術方案間組件的通用性和復用率,整體降低視覺改版的研發成本。

一直以來,外賣業務都處于高速發展階段,人員規模在不斷擴大,項目復雜度在持續增加。目前平臺承載了美團餐飲、商超、閃購、跑腿、藥品等多個業務品類,用戶入口也覆蓋了美團App外賣頻道、外賣App、大眾點評等多個獨立應用。因為客戶端一直比較側重業務開發,為了滿足業務快速上線的需求,UI組件并沒有統一的實現,而是分散到各個業務場景中,在開發過程中因UI缺乏同一的標準而導致以下問題不斷凸顯:

UI/UE層面

① UI缺乏標準化的設計規范,在不同App及不同語言平臺上設計風格不統一,用戶體驗不一致。

② 設計資源與代碼均缺乏統一的管理手段,無法實現積累沉淀,無法適應新業務的開發需求。

RD層面

① 組件代碼實現碎片化,存在多次開發的情況,質量難以得到保證。

② 各端代碼API不統一,維護拓展成本較高,變更主題、適配Dark Mode等需求難以實現。

QA層面

重復走查,頻繁回歸,每次發版均需驗證組件質量。

PM層面

版本迭代效率低,版本需求吞吐量低,不能滿足業務的快速拓展能力。

基于上述開發工作中的切實痛點,以及未來可預見的對客戶端能力的開發需求,我們迫切需要一套統一的UI設計規范,以此沉淀出設計風格,建立統一的UI設計標準,從而抽離成熟的業務場景,提供高質量、可擴展、可統一配置的同時能基于Android/iOS/MRN/Mach組件開發的代碼庫,且具備支持多業務高層次的代碼復用能力,提高UI業務的中臺能力,使項目具有高度一致性。

我們通過積木Sketch插件來落地設計規范,可以保證設計元素均從既定設計標準中獲取,產出符合業務設計語言的設計稿,而各平臺UI組件庫中也有對應實現,從而使積木插件成為UI一致性的抓手,最終可以減少開發成本,提升交付質量,服務好我們美團的多個業務團隊。

外賣UI一致性項目

2. Sketch & Sketch Plugin

要想保持UI一致性,就不能打破規則。從設計階段顏色的選擇、字體的規范、控件的樣式到RD開發階段代碼的統一管理、API的制定、多端的實現方式,都必須遵守一套規則,而Sketch Plugin建設則是讓規范落地執行的解決方案。

在討論其重要性之前,我們首先簡單介紹一下Sketch:Sketch是一個設計工具包,由總部位于荷蘭海牙的BohemianCoding團隊開發,該團隊成員目前不足百人,來自全球多個國家,通過互聯網遠程協作開發,屬于典型的高效開發團隊。

Sketch容易理解且上手簡單;可與團隊中的每個人創建、更新和共享所有Symbol組件,實現設計資源的共享和版本管理,從此告別“final-final-final-1”;其版本迭代速度非常快,且能不斷添加新功能,滿足用戶的需求,更符合互聯網時代;Sketch可以使用真實數據進行設計。目前,我們設計團隊已經全面使用Sketch進行設計。

設計語言包括Iconfont、色板、文字規范、話術、插畫、動畫、組件等。其實它并不是一個抽象的概念,比如大家提到“美團”就會想起“美團黃”,想到可愛的“袋鼠”,想到那些騎著摩托車、穿著印有“美團外賣”亮黃色衣服的騎手小哥。通過設計語言,我們可以更好地傳達品牌主張和設計理念。UI團隊逐步將設計語言沉淀為設計規范,并將其量化內置于積木Sketch Plugin中,使產出的設計稿和RD代碼庫中的組件一一對應,從而形成一個完整的閉環,進而可加速整個業務的交付流程。

使用Sketch Plugin可以快速設計出標準頁面

積木Sketch Plugin功能演示

3. 積木Sketch 插件項目

其實,市面上已存在類似插件,為什么我們還要自己動手開發呢?因為UI設計語言與自身業務關聯性很強,不同業務的色彩系統、圖形、柵格系統、投影系統、圖文關系千差萬別,其中任意一環的缺失都會導致一致性被破壞。現有插件所提供的通用設計元素無法滿足外賣設計團隊的需求,開發一款可以與業務強關聯且功能可定制的插件,顯得尤為重要。

此外,統一的品牌符號、品牌特征,也有助于加深產品在用戶心中的印象,統一的顏色和交互形式能幫助用戶加深對產品的熟悉感和信任感,一個好的設計語言本身可以在體驗上為產品加分,也能夠更好創造一致性的體驗。

積木Sketch插件經過一段時間的建設,目前已具備Iconfont、標準色板、組件庫、數據填充、文字模板等功能。

我們通過Iconfont可以從美團的圖標庫中拉取設計團隊上傳的SVG圖標,并直接應用于設計稿;標準色板可以限定設計師的顏色使用范圍,確保設計稿中的顏色均符合設計規范;組件庫中包含從外賣業務中抽離的基本控件與通用組件,具有可復用和標準化的特點,并與不同語言平臺組件庫中的代碼一一對應,使用組件庫中的組件進行設計,可以提升UI的設計效率、開發效率以及走查效率;數據填充庫可以實現圖片填充和文本填充,圖片包含了商品及商家素材,文字則包含了菜品、商鋪名等信息,通過數據填充可以使設計師采用真實數據進行填充,讓設計稿更為直觀,也更貼近線上環境;文字模板中內置了Head、SubTitle、Body、Caption的使用規范,根據設計稿中文字的位置,點擊文字圖層即可直接應用字體、行高、字距等屬性。

此外,我們還根據設計同學的使用反饋,不斷增添新功能。同時也在拓展插件的使用場景,增加業務線切換功能,使積木插件可以為更多的團隊服務,并期待它能成為更多設計師的“貼心搭檔”。

積木Sketch Plugin已支持功能

4. 為什么要寫這篇文章?

相信你讀完上面的內容,肯定迫不及待的想了解一下Sketch插件,以此迅速提升自己團隊開發效率了吧?

其實在開始之前,我們可先了解一些不利的條件。第一點,由于Sketch更新速度極快,但是官方文檔卻十分簡單且陳舊,因此很多知名的Sketch Plugin因每次API的變更過大紛紛放棄維護;第二點,由于開發技術棧混亂,成熟項目一般還未開源,而開源的項目基本上沒有什么參考價值,絕大多數都是“update 3 years ago”;最后一點,macOS開發資料更是少的可憐。

我們閱讀了大量的文檔卻沒有理清頭緒,仿佛很多Wiki講到關鍵地方,比如某個非常期待的功能是怎么實現的時候,作者竟然一筆帶過,讓人摸不到頭腦。知乎上一篇Sketch Plugin的科普文,很多網友會評論“求教學視頻,我可以花錢買的”。經過一步步踩坑,我們就總結了一些開發經驗,為了避免大家“重復踩坑”,晚上可以早點下班陪陪家人,我們決定寫一篇文章記錄下開發的過程。雖然比起那些已經更新多版的成熟項目,但還有不少的差距,至少可以讓大家不再那么迷茫。

當然,即使你覺得自己是個“跟Sketch八竿子打不著”的開發同學,我們也覺得這篇文章同樣也值得閱讀,因為你會通過本文接觸到前端、移動端、桌面端、服務端的各種開發知識。我們都知道,越來越多的公司開始喜歡招全棧工程師,像Facebook基本上只招全棧工程師。你心里是不是在想:“是不是在搞笑啊?不過一個插件而已?”先別輕易下結論。

準備好了嗎?盤它!

準備放手Coding之前

好,先別著急敲擊鍵盤。畢竟我們連使用哪種語言去開發都沒決定,這曾經也是困惱我們許久的一個問題。目前Sketch Plugin開發大概有兩種方式:

① 使用JavaScript + CocoaScript的混合開發模式,Sketch團隊官方維護了一套JS API,并在開發者官網寫了一句非常振奮人心的話:“ Take advantage of ES6, access macOS frameworks and use the Sketch APIs without learning Objective-C or Swift.”

理想很美滿,但現實很骨感。這個API目前還不算完善,很多功能無法實現,因此我們需要搭配CocoaScript訪問更豐富的內部API。

② 直接采用Objective-C 或Swift,并搭配macOS的UI框架AppKit進行開發,簡單粗暴,并且可以利用OC運行時直接調用Sketch內部API。但這里要特別提醒一下,你要承擔的風險是:隨著Sketch的不斷更新,內部API的命名和使用方式可能會發生較大變化,很多知名插件都因此放棄更新

本文采用了“混合開發模式”進行講解,希望能夠給你一些小啟發。

Sketch 開發原理

1. Sketch Plugin開發流派

2. 環境配置

Skpm(Sketch Plugin Manager)是Sketch提供的用于Plugin創建、Build以及發布的官方工具。Skpm采用Webpack作為打包工具,當然如果你對前端知識足夠熟悉,也可以采用Rollup或者roadhog。但是,為了防止遇到各種各樣的報錯,這里并不建議你這么做。

Skpm提供了一系列幫助快速入門的模板,最有用的莫過于skpm/with-webview,它可以幫助我們創建一個基于WebView展示的Demo示例,而且Skpm會在構建完成后,自動創建一個Symbolic Link將插件添加到Sketch的安裝目錄,使Plugin立即可用。

//基于webpack的Sketch官方打包工具skpm npm?install?-g?skpm //創建示例工程 skpm?create?my-plugin?--template=skpm/with-webview //Install?the?dependencies npm?install //構建插件 npm?run?build

3. 項目結構

Plugin Bundle

按照上面的步驟操作完成后,我們會得到如下插件目錄,它以標準化的分層結構存儲了源碼文件以及構建生成的Sketch插件安裝包。這里沒有使用官方文檔中最簡單的Demo,而是使用目前開發中最為常用的With-Webview模板進行分析,以免出現學完“1+1”后遇到的全是“微積分”問題,并且大部分插件均是在此基礎上進行拓展。

目錄中的參數,相信你在看完注釋后馬上就能明白。可是如果此前沒有前端開發經驗,可能不了解在經過Webpack打包后,腳本文件的文件名會發生變更,比如resources中的webview.js經過打包后會儲存在插件的Resources文件夾中,而文件名則變更為resources_webview.js,因此在進行代碼編寫時,如果需要在html中引用此文件,也要使用打包后的文件名,即:<script src="../resources_webview.js"></script>。這里有個小技巧,如果你不知道腳本文件打包后的文件名及路徑,建議先使用Webpack進行編譯,然后查看其在打包后的Plugin中的位置和名稱,然后再進行引用。

├──?assets?//資源文件夾,如需更改需在package.json中的skpm.assets中設置? ├──?my-plugin.sketchplugin???//skpm構建過程生成的插件包 │???└──?Contents │???????├──?Resources │???????│???└──?_webpack_resources │???????│???└──?resources_webview.js │???????│???└──?resources_webview.js.map │???????└──?Sketch │???????????├──?manifest.json │???????????├──?__my-command.js │???????????└──?__my-command.js.map ├──?package.json ├──?webpack.skpm.config.js ├──?resources?//資源文件 │??├──?style.css │??├──?webview.html │??└──?webview.js └──?src?//需要被webpack打包的腳本文件以及manifest清單文件├──?manifest.json└──?my-command.js

Manifest

你沒有看錯!plugin中也有manifest.json,它與其它平臺比如Android開發中的清單文件意義相同。清單文件記錄了作者信息、描述、圖標以及獲取更新的途徑等等。想想看,每天熬夜加班寫代碼,總得有個地方把你的名字記錄下來吧。但manifest最重要的作用其實是告訴Sketch如何運行插件,以及如何將插件集成進Sketch的菜單欄中。

commands使用一個數組,記錄了插件所提供的所有命令。比如下面的例子,當用戶從菜單欄點擊 “顯示工具欄”這個條目時,就會執行script.js中的function showPlugin() 。menu則提供了插件在Sketch菜單欄中的布局信息,Sketch會在插件被加載時初始化菜單。

{"commands":?[{"name":?"顯示工具欄","identifier":?"roo-sketch-plugin.toolbar","script":?"./script.js","handlers":?{"run":?"showPlugin"}}],"menu":?{"title":?"????外賣積木SketchPlugin工具欄","items":?["roo-sketch-plugin.toolbar"]} }

package.json

簡單來說,只要你的項目中用到了NPM,根目錄下就會自動生成package.json文件。Node.js項目遵循模塊化的架構,package.json定義了這個項目所需要的各種模塊以及配置信息。使用npm install命令會根據這個配置文件,自動下載所需的模塊,也就是配置項目所需的運行和開發環境。

非常值得稱贊的是,Plugin開發中對于網絡請求、 I/O 操作以及其它功能,可以使用與Node.js兼容的polyfill,其中許多常用modules已經預裝到了Sketch中,比如console、fetch、process、querystring、stream、util等。

這里你只需要知道以下幾點:

  • 需要參與Webpack打包的腳本文件必須在resources目錄下聲明,否則不會參與編譯(重點!考試要考!)。

  • assets目錄需要配置在skpm.assets下。

  • 常用的命令可以定義在scripts中方便直接調用。

  • dependencies字段指定了項目運行所依賴的模塊,devDependencies指定項目開發所需要的模塊。

{"name":?"roo-sketch-plugin","author":?"hanyang","description":?"外賣積木Sketch?plugin,UI同學好喜歡~","version":?"0.1.0","skpm":?{"manifest":?"src/manifest.json","main":?"roo-sketch-plugin.sketchplugin","assets":?["assets/**/*"]},"resources":?["src/webview/template/webview.js"],"scripts":?{"build":?"rm?-rf?roo-sketch-plugin.sketchplugin?&&?NODE_ENV=development?skpm-build",},"dependencies":?{},"devDependencies":?{} }

4. API Reference

JavaScript API

由于使用了與Safari相同的JS引擎,Plugin腳本可以獲得完整ES6支持。官方的JavaScript API由Sketch團隊維護,并允許訪問和修改Sketch文檔,通過API可以向Sketch用戶提供數據并提供一些基本的用戶界面集成。

//訪問、修改和創建文檔從color到layer再到symbol等方方面面 var?sketchDom?=?require('sketch/dom') //對于異步操作,JavaScript?API提供了fibers延長contex的lifeTime var?async?=?require('sketch/async') //直接在Sketch中提供圖像或文本數據,DataSupplier直接與Sketch用戶界面集成。 var?DataSupplier?=?require('sketch/data-supplier') //無需重新build的情況下顯示通知以及獲取用戶輸入 var?UI?=?require('sketch/ui') //保存圖層或文檔的自定義數據,并存儲插件的用戶設置。 var?Settings?=?require('sketch/settings')

CocoaScript Syntax

CocoaScript通過賦予了JavaScript調用Sketch內部API以及macOS Cocoa frameworks的能力,這意味著除了標準的JavaScript庫外,還可以使用許多很棒的類與函數。CocoaScript建立在蘋果的JavaScriptCore之上,而JavaScriptCore是為Safari提供支持的JavaScript引擎。

因此,當你使用CocoaScript編寫代碼的時候,你就是在寫JavaScript。CocoaScript中的Mocha實現JS到Objective-C的Bridge,雖然Mocha包含在CocoaScript中,但文檔仍保留在原始Github中。因此,你在CocoaScript的Readme中看不到任何語法教程。這里一個訣竅是,如果你想了解Mocha將原生的Sketch Objects通過bridge,從Objective-C傳遞到JavaScript層的屬性、類或者實例方法的信息,可以將其通過console打印出來:

let?mocha?=?context.document.class().mocha() console.log(mocha.properties()) //OC [executeOperation:withObject:error:] //CocoaScript executeOperation_withObject_error()

通過CocoaScript 提供的Bridge使用JavaScript調用Objective-C的基本語法如下:

  • Objective-C的方括號語法“[ ?]”轉換為JavaScript中的點“ . ”語法。

  • Objective-C的屬性導出到JavaScript時Getter為object.name() ?而Setter為object.name = 'Sketch'。

  • Objective-C的selectors被暴露為JavaScript 的代理方法。

  • “:” 冒號被轉換為下劃線“ _”, 最后一個下劃線是可選的。

  • 調用帶有一個下劃線的方法需要加倍為兩個下劃線: sketch_method變為sketch__method。

  • selector的每個component被連接成不帶有分隔符的單個字符串。

5. Actions

行為定義

Action指的是由于用戶交互而在應用程序中發生的事件,比如“打開文檔”、“關閉文檔”、“保存”等。Sketch所提供的了Action API可以使插件對應用程序中的事件做出反應,有點類似Android開發中的的BroadCast或者Job Scheduler。官方文檔列舉了數百個可供監聽的Action,但最常用到的只有下面幾個:

監聽回調

我們只需在插件的manifest.json文件中添加一個handler即可。比如下面的例子添加了對于“OpenDocument”的監聽,也就是告訴插件在新文檔被打開時要去執行onOpenDocument這個function。

?{"script":?"action.js","identifier":?"my-action-listener-identifier","handlers":?{"actions":?{"OpenDocument":?"onOpenDocument"}} }

當一個Action被觸發時,會回調JS中的監聽方法,與此同時Sketch可以向目標函數發送Action Context,其中包含動作本身的一些信息。在下面例子中,每次打開文檔時都會彈出一個Toast。

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

6. Bridge雙向通信

在常規的插件開發中,UI層一般采用Webview實現,因此你可以使用各種前端開發框架,比如React或者Vue等;而插件的邏輯層(負責調用Skecth API)顯然不在WebView中,因此需要通過Bridge進行通信。邏輯層將從服務器獲取到的數據傳遞給UI層展示,而UI層則將用戶的操作反饋傳遞給邏輯層,使其調用Sketch API更新Layers。

Sketch 通信原理

插件發送消息到WebView

//On?the?plugin: browserWindow.webContents.executeJavaScript('someGlobalFunctionDefinedInTheWebview("hello")').then(res?=>?{//?do?something?with?the?result})//On?the?WebView: window.someGlobalFunctionDefinedInTheWebview?=?function(arg)?{console.log(arg) }

WebView發送消息給插件

//On?the?webview: window.postMessage('nativeLog',?'Called?from?the?webview') //On?the?plugin: var?sketch?=?require('sketch') browserWindow.webContents.on('nativeLog',?function(s)?{sketch.UI.message(s) })

經過了以上步驟,我們就得到了一個基礎插件,它以WebView作為內容載體,并具有雙向通信功能。打開插件時,Webview會將頁面加載完成的事件傳遞給邏輯層,邏輯層調用Sketch API彈出Toast;點擊Get a random number可以從邏輯層獲取一個隨機數。

skpm/with-webview 運行效果

快來正式加入開發隊伍

相信閱讀完上面的部分,制作一個簡單的插件對于你來說,已經有點“游刃有余”了。但這個時候,疑惑也隨之而來,為什么Demo和我們常用插件的UI差別如此之大?

沒錯,官方文檔只教給我們最基礎的插件開發流程,一個成熟的商業項目絕不僅僅是以上這些。一個功能完善的插件應該包括以下三部分:工具欄、WebView容器以及業務數據。下面,我們會一步步為你展示如何開發一個商業化插件UI,同時也會演示美團外賣“填充功能”的實現(注:篇幅原因文檔中僅保留關鍵代碼。)

常規Sketch插件結構

1. 創建吸附工具欄

所謂吸附式工具欄,就是展示在Skecth右側Inspector Panel旁邊的工具欄,它以吸附的方式與Sketch操作界面融為一體,這也是絕大多數插件的視覺呈現方式。工具欄中展示了當前插件可以提供的大部分功能,方便我們在操作Document時快速選取使用。

開發工具欄主要使用NSStackView、NSButton、NSImage以及NSFont這幾個類,如果沒有開發過macOS應用的同學可能對這些類有些陌生,可以類比iOS開發中以UI作為前綴的控件類,NS前綴主要是AppKit以及Foundation的相關類,MS前綴則是Skecth的相關類,CA、CF前綴為核心動畫庫和核心基礎類。

下面的代碼記錄了創建工具欄的關鍵步驟,更為詳細的操作可以參考一些Github倉庫,比如sketch-plugin-boilerplate等。

const?contentView?=?context.document.documentWindow().contentView(); const?stageView?=?contentView.subviews().objectAtIndex(0);//1.創建toolbar const?toolbar?=?NSStackView.alloc().initWithFrame(NSMakeRect(0,?0,?27,?420)); toolbar.setBackgroundColor(NSColor.windowBackgroundColor()); toolbar.orientation?=?1;//2.創建Button const?button?=??NSButton.alloc().initWithFrame(rect) const?Image?=?NSImage.alloc().initWithContentsOfURL(imageURL) button.setImage(image) button.setTitle("數據填充") button.setFont(NSFont.fontWithName_size('Arial',11))//3.將Button加入toolbar toolbar.addView_inGravity(button,?gravityType);//4.將toolbar加入SketchWindow const?views?=?stageView.subviews() const?finalViews?=?[] for?(let?i?=?0;?i?<?views.count();?i++)?{finalViews.push(view)if(view[i].identifier()?===?'view_canvas'){finalViews.push(toolbar) }stageView.subviews?=?finalViewsstageView.adjustSubviews()

2. 創建WebView容器

除了通過CocoaScript創建原生NSPanel外,這里推薦使用官方的sketch-module-web-view快速創建WebView容器,它提供了豐富的API對窗口的展示樣式和行為進行定制,包括Frameless Window、Drag等,同時還封裝了WebView與插件層的通信的Bridge,使你可以輕松在"frontend" (the WebView)和"backend" (the plugin running in Sketch)之間發送消息。

//(1)方法一:原生方式加入webview const?panel?=?NSPanel.alloc().init(); panel.setFrame_display(NSMakeRect(0,?0,?panelWidth,?panelHeight),?true); const?wkwebviewConfig?=?WKWebViewConfiguration.alloc().init() const?webView?=?WKWebView.alloc().initWithFrame_configuration(CGRectMake(0,?0,?panelWidth,?panelWidth),wkwebviewConfig ) panel.contentView().addSubview(webView); webview.loadFileURL_allowingReadAccessToURL(NSURL.URLWithString(url),NSURL.URLWithString('file:///') ) //(2)方法二:使用官方的BrowserWindow import?BrowserWindow?from?"sketch-module-web-view"; const?browserWindow?=?new?BrowserWindow(options); const?webViewContents?=?browserWindow.webContents;webViewContents.executeJavaScript(`someGlobalFunctionDefinedInTheWebview(${JSON.stringify(someObject)})`).then(res?=>?{//?do?something?with?the?result})browserWindow.loadURL(require('./webview.html'))

3. 創建內容頁面

歷盡千辛萬苦,我們終于拿到了WebView,這下就可以發揮你“天馬行空”的想象力了。不管是React還是Vue,亦或只是一些簡單的靜態頁面對于你而言應該都不在話下。在完成界面開發后,只需通過Window向插件發送指令即可。下面的例子演示了積木插件的“數據填充”功能。

UI側

import?React?from?'react'; import?ReactDOM?from?'react-dom';//使用react搭建用戶頁面 ReactDOM.render(<Provider?store={store}><App?/></Provider>,?document.getElementById('root'));//傳遞用戶點擊填充類目給插件層,這里以填充文字為例 export?const?PostMessage?=?(name,?fillData)?=>?{try?{window.postMessage("fill-text-layer",?fillData);}?catch?(e)?{console.error(name,?"出現異常!!!"?+?fillData);} };?

插件側

??browserWindow.webContents.on('fill-text-layer',?function(s)?{//找到當前頁面documentconst?document?=?context.document;//獲取用戶選擇的layersconst?selection?=?Document.fromNative(document).selectedLayers;layers.forEach(item?=>?{//判斷layer類型是否為文字if?(item.type?===?'Text')?{//更新textlayeritem.text?=?value;}});? })

4. 還想加點出彩的功能

如果你還不滿足于此,說明你真的是個很愛學習,也很有潛力的開發同學。一個完善的插件需要包括交互層、API層、業務層、調試層以及發布層,每層各司其職,它們都在默默干好自己的工作。

前面的步驟,通過構件菜單欄、創建Webiew完成了交互層的開發;通過Webview的Bridge傳遞用戶操作到插件側代碼,之后調用Sketch API對圖層進行操作,這是API層的工作;而根據自身需求并依托交互層與API層的實現去編寫業務代碼,則是業務層的工作;至此,你應該就擁有了一個可運行的插件了。

但除此之外,在代碼編寫過程中還需要Lint組件輔助開發,發現問題需要使用各類Dev工具進行調試,通過QA驗證后,需要Cli工具打包并發布插件更新。這一小節,我們將簡單介紹一些基本的調試層和發布層知識。

積木Sketch Plugin結構

Webpack配置

Skpm默認采用Webpack作為打包工具。Webpack是一個現代JavaScript應用程序的靜態模塊打包器(Module Bundler)。當Webpack處理應用程序時,它會遞歸地構建一個依賴關系圖(Dependency Graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個Bundle,需要在webpack.config.js進行配置,類似于Android中的Gradle,同樣支持各種插件。

Webpack處理流程示意

由于插件的開發者未必是前端同學,可能之前并沒有接觸過Webpack,因此我們在這里介紹它的一些常用配置,讓你有更多的時間關注業務代碼。第一次接觸Webpack是在去年一次公司內部的技術培訓上(美團技術學院提供了很多技術培訓課程,加入我們就可以盡情地在知識的海洋中遨游了),美團MRN項目的打包方案就是Webpack。

在前端圈有各種各樣的打包工具,比如Webpack、Rollup、Gulp、Grunt 等等。RN打包用的是Facebok實現的一套叫做Metro的工具,而美團MRN打包工具的選型是Webpack,因為Webpack具有強大的插件機制和豐富的社區生態,可以完成復雜的流水線打包工作,Webpack在Plugin開發中同樣發揮了非常重要的作用。Webpack有五個核心概念:

在插件開發中需要處理html、css、sass、jpg、style等各種文件,只有在Webpack中配置相應的Loader后,這些文件才能被處理。而且我們很可能遇到某些文件需要使用特定的插件,而其它文件又無需處理的情況。下面的示例中列舉了添加插件、對文件單獨處理以及參數配置這三個常用的基本操作。

module.exports?=?function?(config,?entry)?{?//常用功能1:增加插件config.module.rules.push({test:?/\.(svg)([\?]?.*)$/,use:?[{loader:?"file-loader",options:?{outputPath:?url?=>?path.join(WEBPACK_DIRECTORY,?url),publicPath:?url?=>?{return?url;}}}]});}//常用功能2:對文件單獨處理 if?(entry.script?===?"src/script.js")?{config.plugins.push(new?htmlWebpackPlugin({?})); }//常用功能3:定制js處理config.module.rules.push({test:?/\.jsx?$/,use:?[{?loader:?"babel-loader",options:?{presets:?["@babel/preset-react","@babel/preset-env"],plugins:?[//引入antd組件庫["import",{libraryName:?"antd",libraryDirectory:?"es",style:?"css"}]]}}]});

ESLint配置

JavaScript是一門非常靈活的語言,很多錯誤往往運行時才爆出,通過配置前端代碼檢查方案,在編寫代碼過程中可直接得到錯誤反饋,也可以進行代碼風格檢查,不僅提升了開發效率,同時對不良代碼編寫習慣也能起到糾正作用。在ESLint中需要配置基礎語法規則、React 規則、JSX規則等,由于Sketch插件的CocoaScript語法較為特殊,需要配置全局變量以此忽略AppKit中無法識別的類。

雖然,我們曾在部門組會中被多次“安利”ESLint的強大作用(這里給大家推薦一篇技術文章:ESLint 在中大型團隊的應用實踐),但如果不是做前端或者RN開發的同學,可能對于ESLint的復雜配置并不熟悉。可以直接使用Skpm提供的ESlint Config,里面配置了包含Sketch和macOS的頭文件的全局變量,而代碼格式化則推薦使用Prettier。

npm?install?--save-dev?eslint-config-sketch //或者直接使用帶prettier以eslint的skpm?template工程 $?skpm?create?my-plugin?--template=skpm/with-prettier

內容服務端化

Sketch推出的庫(Library)功能對于維護設計系統或風格指南,起到非常重要的作用,可以給團隊帶來高效工作體驗,甚至改變設計團隊工作方式和流程。我們通過組件庫可以在整個設計團隊中共享組件(Symbol),Library可以實現“一處更改,處處生效”,即使是關聯了遠程組件庫歷史的設計稿檢測到更新時,也會收到Sketch通知,確保工作中使用的是最新組件。

庫功能對美團外賣UI一致性起著至關重要的作用,這主要體現在兩方面:首先是實現設計風格沉淀,目前袋鼠UI已經形成了自己的獨特風格,外賣設計團隊根據設計規范,對符合UI一致性外賣業務場景的組件不斷進行抽象及建設,沉淀出越來越多的通用業務組件,這些組件需要及時擴充到Library中,供團隊成員使用;另外一個作用,則是保持團隊使用的均為最新組件,由于各種原因,組件的設計元素(色彩、字體、圓角等屬性)可能會發生變更,需要及時提醒團隊成員更新組件,保持所有頁面的一致性。

Sketch內置的iOS遠程組件庫

Library中的Symbol提示更新

庫組件自動更新,其實就是 “庫列表” - “庫 ID” - “外部組件原始 ID” 這三者的關聯。Sketch內部是靠UUID進行對象識別的,通過庫組件的庫ID,從庫面板的列表中,按照添加的時間從新到舊依次檢索所有未被禁用的、鏈接完好的庫,直到匹配到庫的ID ,然后查找該庫文件內是否有與庫組件SymbolID匹配的組件,如果包含且內容有差異就提醒更新,更新的過程實際上是內容替換。

我們通過以下步驟使用RSS技術共享Library供整個UI設計團隊使用:

  • 將Library Document 托管到公司內網服務器上。

  • 創建一個XML文件記錄版本信息和更新地址。

  • 最后使用Meyerweb URL編碼器之類的工具(或直接encodeURIComponent)對XML feed URL進行編碼并將其添加到以下內容:sketch://add-library?url=https://***.xml。

  • 將此URI在瀏覽器中打開即可。

<?xml?version="1.0"?encoding="UTF-8"?> <rss?version="2.0"?xmlns:atom="http://www.w3.org/2005/Atom"?xmlns:content="http://purl.org/rss/1.0/modules/content/"?xmlns:dc="http://purl.org/dc/elements/1.1/"?xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"><channel><title>My?Sketch?Library</title><description>My?Sketch?Library</description><image><url></url></image><item><title>My?Sketch?Library</title><pubDate>Wed,?23?Jun?2019?11:19:04?+0000</pubDate><enclosure?url="mysketchlibrary.sketch"?type="application/octet-stream"?sparkle:version="1"/></item></channel> </rss>

5. 開發流程小結

前面一口氣講述了很多內容,可能你一時無法消化。別急,慢慢來。我們這里對插件的開發流程作個簡要的總結:

  • 首先利用JavaScript 或CocoaScript開發操作面板。

  • 使用NPM安裝所需依賴。

  • 通過Bridge傳遞用戶操作到插件邏輯側,通過調用Skecth API對文檔進行處理。
    使用Webpack進行打包。

  • 通過測試后發布插件更新。

Sketch Plugin開發流程

別人可能沒告訴你的事兒

這部分主要記錄了積木Sketch Plugin開發過程中的踩坑經歷,但是這里,我們沒有貼大段的代碼,沒有直接告訴你答案,而是把分析問題的過程記錄下來。“授人以魚不如授人以漁”,相信只要你了解了這些分析技巧,即使之后遇到更多的問題,也可以輕(jia)松(ban)解決。

1. 與Xcode工程混合編譯

首先,我們要明確一個問題,為什么要使用XCode工程?

雖然官方提供了JS API并承諾持續維護,但這項工作一直處于Doing狀態,而且官方文檔更新緩慢,沒有明確的時間節點。因此,對于某些功能,比如我們想建一個具有Native Inspector Panel的插件,就不得不使用XCode進行開發。使用Xcode開發對于iOS開發者也更加友好,無需再學習前端界面開發知識。

這里推薦Invison的開發成員James Tang分享的博客文章《Sketch Plugin Xcode Template》,里面詳細描述了構建插件XCode工程的步驟,這也成為很多插件開發者遵循的范本。當然隨著Sketch的不斷升級,某些API已經不受支持,但作者講述的開發流程和思路依然沒有改變,具有很高的學習價值。

JavaScript //利用?Mocha加載framework var?mocha?=?Mocha.sharedRuntime(); [mocha?loadFrameworkWithName:frameworkName?inDirectory:pluginRootPath]

除此之外,Skpm中已經內置了@skpm/xcodeproj-loader,也可在JS中直接加載Framework。

JavaScript //加載framework const?framework?=?require('../xcode-project-name/project-name.xcodeproj/project.pbxproj'); const?nativeClass?=?framework.getClass('NativeClassName'); //獲取nib文件 const?ui?=?framework.getNib('NativeNibFile'); //也可以直接加載xib文件 const?NibUI?=?require('../xcode-project-name/view-name.xib') var?nib?=?NibUI() let?dialog?=?NSAlert.alloc().init() dialog.setAccessoryView(nib.getRoot()) dialog.runModal()

當然你也可以直接使用Github上一些知名的開源項目,有些會直接提供Framework供你使用,比如更改原生的toolbar:

2. 了解Electron

為什么在講述Sketch Plugin的時候,忽然會提到Electron?這里有一個小故事,某天上班打開大象(美團內部溝通軟件)。

MacOS版大象截圖

看到一條公眾號推送,是公司成立了Electron技術俱樂部(美團技術團隊內部自發成立了很多技術俱樂部),經過了解發現Electron基于Chromium和Node.js,可以使用HTML、CSS和JavaScript構建桌面應用程序,Electron負責其中比較復雜的部分,而開發者只需關心應用的核心需求即可。大象的Mac端就大量使用了Electron技術,用Web框架去開發桌面應用,可以直接復用Web現有的開發成果并獲得出色的運行效率。

我們就進行了簡單的學習,在之后的一段時間并沒有再去關注這項技術,直到某天在插件開發的過程中忽然遇到一個問題:在插件WebView顯示的情況下,在桌面空白處點擊使Sketch軟件失去焦點,整個App就會被隱藏。試了幾個流行的插件,發現大部分均有此問題,這給設計師的工作造成了諸多不便。試想,我只是去打開Finder找一個文件,你為什么要把我的軟件最小化?在Github上留言后,很快得到了項目開發者Mathieu Dutour的官方回復,原來只需要設置一個hidesOnDeactivate屬性即可。

等等!這不是Electron中的屬性么?仔細查看Readme才發現作者寫道“The API is mimicking the BrowserWindow API of Electron.”這下可方便多了!你想自定義窗口的表現,只需按照Electron的API設置即可,想想看其實Electron的工作方式是不是和Sketch Plugin如出一轍?

3. 更新原生屬性面板

為了更好地提升積木Sketch Plugin的使用體驗,UI同學通過建立公共Wiki記錄我們設計團隊在插件使用過程中的反饋建議,其中有一條很奇怪:“通過插件面板更新Layer屬性后,右側面板不刷新。”和上一個問題一樣,經測試其它插件大部分也有此問題,但是如何去更新右側屬性面板呢?翻閱了Sketch的API文檔還是“丈二和尚,摸不著頭腦”。這個時候想起了macOS開發的一個神器Interface Inspector,它可以在運行時分析正在運行的Mac應用程序的界面結構和屬性,非常強大。

開心的下載下來后,發現這個軟件上次的更新時間是6年前,忽然有了一種不祥的預感。果然Attach任何App時都會提示無法Attach,在macOS Catalina版本已經無法運行。可是這怎么能難倒“萬能”的程序員呢?我們查看系統報錯,發現是mach_inject_bundle_stub錯誤,查閱發現mach_inject_bundle_stub是Github上的一個開源庫,所以自己下載源碼重新編譯個Bundle包就可以了。

Attach成功后,就可以對Sketch的面板進行屬性分析了,是不是忽然感覺打開了新世界的大門?經過查閱發現右側面板在MSInspectorController中。如下圖所示:

Interface Inspector對Sketch進行運行時分析

下一步需要用Class-Dump工具來提取Sketch的頭文件,查看可以對inspector面板進行操作的所有方法:

通過class-dump得到的頭文件

不出所料,我們發現了reload(),猜測調用這個方法可以刷新面板,測試一下發現問題被修復了。如果你使用Sketch的JavaScript API的話,名稱不一定能完全對應,但是基本差不多,稍加分析也可以找到。這里只是教大家一個思路,這樣即使遇到其它問題,按照上面的步驟試試看,沒準就可以解決。

JavaScript //?reload?the?inspector?to?see?the?changes? var?sketch?=?require('sketch') var?document?=?sketch.getSelectedDocument() document.sketchObject.inspectorController().reload()

歡迎你的加入

如你所見,積木Sketch Plugin可以幫助設計團隊提升設計效率、沉淀設計語言以及減少走查負擔;讓RD同學面對新項目時,可以專注于業務需求而無需把時間耗費在組件的編寫上;減少QA工作量,保證控件質量無需頻繁回歸測試;幫助PM提高版本迭代效率及版本需求吞吐量,提供業務的快速拓展能力。

當然,我們除了希望制作一流的產品,也希望積木插件可以讓你在繁忙的工作中得以喘息。我們會繼續以設計語言為依托,以Skecth Plugin為抓手持續進行UI一致性建設,提高客戶端UI業務中臺能力。

可能對于一個前端工程師來說,對React、Webpack等配置可以信手拈來;對于一個iOS工程師來說,XCode調試、Objective-C語法是開發前的基礎;對于一個桌面工程師來說,對Electron、Hook分析已司空見慣。可Sketch Plugin開發就是這么有趣,雖然只是一個小小的插件,但它會讓你接觸各個端的技術,提升技術視野,但同樣會讓你在開發過程中遇到很多困難,曾經困擾了我好幾天的一個Webpack問題,部門同事幫我們聯系了一個開發經驗豐富的前端妹子去咨詢,對方一行代碼竟然就解決了。做你害怕做的事,然后你會發現,不過如此。

目前,積木插件開發還處于較為初級的階段,包括Mach(外賣自研動態化框架)實時預覽、模板代碼自動生成、自建插畫庫等功能已經在路上。除此之外,我們還規劃了很多激動人心的功能,需要制作更多精美的前端頁面,需要更完善的后臺管理。

這里加個廣告吧!不管你是FE、Android、iOS、后端,只要你對Bug毫不手軟,精益求精,都歡迎你加入我們外賣技術團隊,跟我們一起完善Sketch插件生態,讓積木插件可以為更多業務場景提供服務,為用戶提供卓越的體驗。讓我們一起用“積木”拼出萬千世界!

嗯,就先寫到這里吧!UI團隊同學說我們的實現和設計稿竟然差了一個像素,我們要回去改Bug了。

致謝

特別感謝優秀的設計師昱翰、沛東、淼林、雪美,他們在插件開發過程中給予的幫助。

特別感謝技術團隊的云鵬、曉飛在技術上給予的指導。

“前人栽樹,后人乘涼。”我們向優秀開源項目開發者致敬。

參考文獻

| Sketch Plugin開發官方文檔
| 深入理解Sketch庫
| 凹凸實驗室高大師Sketch插件開發實踐
| Sketch Plugin Xcode Template
| Beginning Sketch Plugins Development in Xcode
| 攜程機票Sketch插件開發實踐

----------? END? ----------

招聘信息

美團外賣長期招聘 Android、iOS、FE 高級/資深工程師和技術專家,歡迎加入外賣App大家庭。歡迎感興趣的同學發送簡歷至:tech@meituan.com(郵件標題注明:美團外賣技術團隊)

也許你還想看

|?微前端在美團外賣的實踐

|?美團外賣前端容器化演進實踐

|?Android視頻技術探索之旅:美團外賣商家端的實踐

總結

以上是生活随笔為你收集整理的积木Sketch Plugin:设计同学的贴心搭档的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。