Figma 插件开发 - Vite 环境搭建

Figma 插件开发 - Vite 环境搭建,第1张

最近工作主要和 Figma 插件打交道,梳理一些踩坑的经验~

开始

官方的起始例子:www.figma.com/plugin-docs…

按步骤将插件文件保存到本地即可,调试时可以右键唤起插件,可以关注下几个功能入口:

Import plugin from manifest 导入本地插件Open console 控制台调试Run last plugin 加载最新的插件

目前 figma 插件开发流程没有有效的 hot reload 机制,【加载最新插件】在开发时比较常用,快捷键可以记一下。

插件架构

Figma 的插件架构比较简单,主要关注三部分:

manifest.json 插件清单ui.html 入口core.js 入口

manifest.json

{
  "name": "test",
  "id": "1095700741264679376",
  "api": "1.0.0",
  "main": "code.js",
  "editorType": [
    "figma"
  ],
  "ui": "ui.html"
} 

ui.html

Rectangle Creator

Count:

core.js

figma.showUI(__html__);
figma.ui.onmessage = msg => {
  if (msg.type === 'create-rectangles') {
    const nodes: SceneNode[] = [];
    for (let i = 0; i < msg.count; i++) {
      const rect = figma.createRectangle();
      rect.x = i * 150;
      rect.fills = [{type: 'SOLID', color: {r: 1, g: 0.5, b: 0}}];
      figma.currentPage.appendChild(rect);
      nodes.push(rect);
    }
    figma.currentPage.selection = nodes;
    figma.viewport.scrollAndZoomIntoView(nodes);
  }
  figma.closePlugin();
}; 
manifest

清单描述文件主要描述应用入口、支持编辑器类型还有一些权限相关的配置:

main 用于指定 figma 主应用进程的脚本文件,有点类似 chrome 插件中 background 进程ui 用于直指定插件交互面板 html 入口,类似 chrome 插件的 popup 页面editorType 用于指定插件支持的编辑器类型,编辑的类型有两种,具体的看区别这里 core

core.js 运行在 figma 应用所提供的沙箱环境,其只能使用 figma 所提供 API 与一些基本的 Javascript 特性包括标准类型、JSON 和 Promise API、与 Uint8Array 一类的二进制对象。,DOM 一类的接口是无法使用的,在 core 中尽量使用 figma 提供的 api 来实现目标 *** 作,一些依赖 DOM API 的 *** 作可以考虑交由 ui 页面来处理。

ui

UI 主要用于插件与用户的交互,本质是一个 figma 主页面中内嵌的 iframe 页面,其使用 postmessage 与 core 进行交互。

附:之前为了方便 core 与 ui 通信还写了个页面通讯工具库 rpc-shooter 有兴趣的同学可以瞧瞧。

想具体了解插件的运行机制可以戳 How Plugins Run。

环境搭建

Vite 这段时间使用下来体验十分友好,下面以 Vite 为例搭建一个 Figma 插件开发环境。

示例的仓库地址:github.com/kinglisky/f…

yarn create vite 

这里选用的 Vue 与 TS,框架随意,Figma 插件开发建议使用 TS,接入 figma plugin 的 d.ts 可以方便的当文档使用。

figma plugin 的类型支持需要配置下 tsconfig.json

yarn add @figma/plugin-typings -D 
{
  "compilerOptions": {
   ...other,
    "typeRoots": ["./node_modules/@types", "./node_modules/@figma"]
  }
} 
多入口

Figma 插件中我们只关心两个入口文件:

ui.htmlcore.js

两个入口文件相互独立,需要将 ui 与 core 拆分成两个包来维护,整理下目录结构拆分出 ui 与 core 部分,示例中还拆分了一个 common 包用于提供公共配置:

.
├── lerna.json
├── manifest.json
├── package.json
├── packages
│   ├── common
│   │   ├── constants
│   │   │   └── ui.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── core
│   │   ├── favicon.svg
│   │   ├── index.html
│   │   ├── package.json
│   │   ├── src
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── vite.config.ts
│   └── ui
│       ├── index.html
│       ├── package.json
│       ├── src
│       │   ├── App.vue
│       │   ├── env.d.ts
│       │   ├── main.ts
│       │   └── styles
│       │       └── index.css
│       ├── tsconfig.json
│       ├── tsconfig.node.json
│       └── vite.config.ts
├── scripts
│   └── build.js
├── tsconfig.json
└── yarn.lock 

github.com/kinglisky/f…

workspaces

本地进行多包开发时,workspaces 是个很好用的功能,可以将本地包作为依赖来使用,有点类似 webpack 的中 alias 配置将依赖指向本地。

将 packages 目录下的所有包做作为本地依赖:

{
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  ...
} 

插件中实际 core 与 ui 都会从 common 中读配置,common 包的 package.json 配置下 name 与 module 即可提供给 core 与 ui 使用:

{
  "name": "figma-vite-common",
  "private": false,
  "version": "0.0.0",
  "module": "./",
  "types": "./"
} 

core 与 ui 使用时:

import { VIEW_WIDTH, VIEW_HEIGHT } from 'figma-vite-common/constants/ui'; 
构建配置

有一点需要注意一下,core.js 与 ui.html 作为插件的入口文件只能是个单文件,简单来说就是不能拆分文件,不能使用异步模块导出多个文件。

core 中所有依赖的资源都打包到 core.jsui.html 中所有的资源都会以内联形式存在 core 配置

core 的配置比较简单:

import { resolve } from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      formats: ['iife'],
      name: 'core',
      fileName: 'core',
    },
    outDir: resolve(__dirname, '../../dist'),
  },
}); 

由于不需要发包使用,将最终打包的 core 入口格式配置为 iife (立即执行的函数表达式模块),简单的包一层立即调用函数即可:

// 构建结果
(function () {
 'use strict';
  figma.showUI(__html__, { width: 400, height: 490 }),
    (figma.ui.onmessage = (o) => {
      if (o.type === 'create-rectangles') {
        const t = [];
        for (let i = 0; i < o.count; i++) {
          const e = figma.createRectangle();
          (e.x = i * 150),
            (e.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 } }]),
            figma.currentPage.appendChild(e),
            t.push(e);
        }
        (figma.currentPage.selection = t),
          figma.viewport.scrollAndZoomIntoView(t);
      }
      figma.closePlugin();
    });
})(); 

对应的配置下 package scripts:

{
  "name": "figma-vite-core",
  "private": false,
  "version": "0.0.0",
  "scripts": {
    "start": "tsc && vite build -w",
    "build": "tsc && vite build"
  },
  "dependencies": {
    "figma-vite-common": "*"
  },
  "devDependencies": {
    "@figma/plugin-typings": "^1.42.1",
    "typescript": "^4.5.4",
    "vite": "^2.8.0"
  }
} 

开发时会以 yarn start 启动, -w 监听文件变动触发构建。

ui 配置

ui 由于最终的构建产物是一个 html 文件,其他的构建的资源需要内联,这里需要将各种代码拆分的配置关闭,内联资源的阈值调大:

import { resolve } from 'path';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { viteSingleFile } from 'vite-plugin-singlefile';

export default defineConfig({
  plugins: [vue(), viteSingleFile()],
  build: {
    target: ['es6'],
    assetsInlineLimit: 100000000,
    chunkSizeWarningLimit: 100000000,
    cssCodeSplit: false,
    brotliSize: false,
    outDir: resolve(__dirname, '../../dist'),
    rollupOptions: {
      inlineDynamicImports: true,
      output: {
        format: 'iife',
        manualChunks: () => 'everything.js',
      },
    },
  },
}); 

vite-plugin-singlefile 插件用于将构建出的 js 资源内联到 html 中,构建的包格式还是 iife

package scripts 配置:

{
  "name": "figma-vite-ui",
  "private": false,
  "version": "0.0.0",
  "scripts": {
    "start": "vue-tsc --noEmit && vite build -w",
    "build": "vue-tsc --noEmit && vite build"
  },
  "dependencies": {
    "figma-vite-common": "*",
    "vue": "^3.2.25"
  },
  "devDependencies": {
    "@figma/plugin-typings": "^1.42.1",
    "@vitejs/plugin-vue": "^2.2.0",
    "sass": "^1.49.7",
    "typescript": "^4.5.4",
    "vite": "^2.8.0",
    "vite-plugin-singlefile": "^0.6.3",
    "vue-tsc": "^0.29.8"
  }
} 

同样使用 yarn start 启动,-w 监听变动构建。

启动入口

实际在插件开发时需要同时启动 core 与 ui,所以可以在根目录下配置个启动命令,可以直接使用 lerna 进行启动。

配置 lerna.json

{
  "packages": ["packages/*"],
  "version": "0.0.0",
  "npmClient": "yarn",
  "useWorkspaces": true
} 

配置 package.json

{
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "start": "lerna run --stream --scope figma-vite-ui --scope figma-vite-core start"
  },
  "publishConfig": {
    "access": "public"
  },
  "devDependencies": {
    "lerna": "^4.0.0"
  },
  "name": "fgima-vite"
} 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i1gEnVZq-1653620813636)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4767aab3122348879f3945c74209cb85~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

最后改一下插件manifest.json 中入口文件的路径:

{
  "name": "figma-vite-demo",
  "id": "",
  "api": "1.0.0",
  "main": "dist/core.iife.js",
  "ui": "dist/index.html",
  "editorType": ["figma"]
} 

自此一个简单 Figma 插件开发环境完成,仓库在这 github.com/kinglisky/f…

其他

有空会梳理下 Figma 文档数据结构与一些解析技巧,先这样~

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/web/1322589.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-12
下一篇2022-06-12

发表评论

登录后才能评论

评论列表(0条)

    保存