前言
好,先别着急敲击键盘。毕竟我们连使用哪种语言去开发都没决定,这曾经也是困恼我们许久的一个问题。目前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的命名和使用方式可能会发生较大变化,很多知名插件都因此放弃更新。
本文采用了“混合开发模式”进行讲解,希望能够给你一些小启发。
image.png1. Sketch Plugin开发流派
image.png2. 环境配置
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,但最常用到的只有下面几个:
image.png监听回调
我们只需在插件的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。
image.png插件发送消息到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可以从逻辑层获取一个随机数。
image.pngSketch插件开发汇总图
image.pngSketch解析方案参考资料
- AirBnb 的react-sketchapp:提供了UI框架和项目脚手架(github),不提供解析源码相关功能
- 58同城的Picasso:提供了解析代码的思路(博客)
- sketch-node-parser:使用纯NodeJS从草图中解析文件,资料少,更新慢
- sketch-to-html:使用纯NodeJS从草图中解析文件,不能生成标注图
- sketch-meaxure:可以生成标注图和代码片段的信息,满足部分业务需求
官方参考链接:
翻阅过的sketch资料
1、Sketch 插件开发实践总结
2、浅谈sketch插件开发(三)
3、Airbnb - React Sketch App
4、58同城Picasso
5、掘金sketch插件开发指南
6、知乎Sketch插件开发指南
7、Sketch webView方式插件开发技术总结
8、从 sketch 中获取颜色、字体组件内容
9、积木Sketch Plugin:设计同学的贴心搭档
10、携程机票Sketch插件开发实践
11、sketch 导出html 的开源项目
网友评论