likes
comments
collection
share

Figma 插件开发入门指南在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Fi

作者站长头像
站长
· 阅读数 103

在很久很久以前,我开发过一个 Figma 插件,它的样子是这样的:

Figma 插件开发入门指南在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Fi

功能是齐全的,但就是太丑了,一直想着优化优化。但一年多过去了,没有任何进展(被前端的 UI 框架吓跑了)。最近,工作上正好有 Figma 插件相关的开发任务,”浅浅”的入门了一下前端,顺便把这个 Figma 插件优化了,优化之后是这样的:

Figma 插件开发入门指南在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Fi

该 Figma 插件链接:www.figma.com/community/p…

是不是感觉高大上了许多!如果你也有 Figma 插件开发的想法,可以考虑继续看下去,这篇文章或许可以帮助到你。

本文会先介绍一些必备的前端基础知识,可以当作 checklist 去学习和了解,再介绍 Figma 插件相关的知识,最后再用上边的 Figma 插件源码作为 demo 进行讲解。

值得说明的是,本文不会包含 Figma 插件新建,导出等等非常基础的知识点,如果有需要的话,可以查看其他的博客。

前端基础知识学习

本节主要介绍 Figma 插件开发的学习路径,并给每项任务的难度给出主观评分(难度最高五颗星:⭐⭐⭐⭐⭐),由于每个人的知识储备不一样,难度评分仅供参考。

  • 学习 Typescript 语言,难度:⭐⭐

    在此之前,自己熟悉的语言主要有三个:Kotlin、Python、Java。而 TypeScript 可以理解成带了 Javascript 语法糖的面向对象语言,所以个人感觉学起来没什么难度。简单看看即可,使用时遇到不会的再复习一下。

    学习网址:wangdoc.com/typescript/

    学习时间:8h ~ 16h

  • JavaScript 的 async/await 语法,难度:⭐

    个人比较喜欢用协程,所以在这里单独推荐学习一下。使用时值得注意的点:需要圈定好 async 的作用域,不然会出现代码没有按预期顺序执行的情况。

    学习方式:搜索引擎

    学习时间:1h

  • npm 的使用,难度:⭐⭐

    前端使用 npm 来实现多模块的管理,学会 npm 的使用非常重要。大致需要学习:npm 基础命令、npm 多模块管理的配置、package.json 文件中各个字段的定义和作用。

    学习方式:ChatGPT

    学习时间:2~3h

  • React 的学习和使用,难度:⭐⭐⭐

    由于自己之前使用过 Jetpack Compose,其和 React 的使用有很大的相似度,所以选择了 React 来进行前端开发。学习 React 时,需要学习 tsx 的语法,以及 React Hooks 函数的使用(useStateuseEffectuseCallbackuseContext ),外加一些简单的 css style 的学习,这些对于入门级的 UI 开发已经足够。

    学习方式:React 官网 && ChatGPT

    学习时间:3h

  • Ant Design 的了解和使用,难度:⭐

    Ant Design 是基于 React 的 UI 组件库,可以极大的提高 UI 开发效率。可以先简单了解一下其支持的组件,具体使用时再复制粘贴对应组件源代码即可。

    使用方式:设计 UI 稿时,复制粘贴 该 Figma 组件库 的相关组件至自己的 Figma 设计稿,书写代码时复制粘贴对应组件代码即可。

    浏览时间:1h

  • 其他前端框架的了解和使用,难度:⭐⭐

    不得不说,现代化的前端开发,使用到的开发框架还不少。可以先简单了解即可,之后参考一下 TextColor2Android 是如何使用的,基本就算学会了。

    需要了解的框架有:Vite (前端项目编译打包的框架)、turbo (编译管理大型项目)、next.js (方便 React UI 调试的开发框架)。

    了解时间:2h

整体来看,前端知识的学习,难度平均 1.8 颗⭐,总耗时 17 ~ 26h,可以根据自己的经验和实际情况安排具体的学习计划。

Figma 插件开发基础知识

本节会罗列在开发 Figma 插件时所涉及到的知识点,覆盖率大概在 95% 左右。当然,一些类似于 SceneNode 属性相关的知识就不会过多介绍了,这些细节的知识得自行查阅 Figma Plugin Docs 获得。

同时,为了更好的理解,强烈建议配合 TextColor2Android 源代码一起食用。

  • 简单理解 Figma 插件的运行原理

    Figma 运行时,分为 Figma 沙盒环境(main)和插件 HTML(ui) 环境。只有在 Figma 沙盒环境(main)中才能访问 Figma 的 API,读写 Figma 的设计稿,两者通过 postMessage 和 onMessage 通信。运行原理详情可查看 How Plugins Run

    由此可以引申到 Figma 插件 manifest.json 配置中 main 和 ui 文件的路径定义:

    {
      "main": "path/to/your/code.js",
      "ui": "path/to/your/index.html"
    }
    

    main(code.js) 文件的逻辑在 Figma 沙盒环境中运行,ui(index.html) 在非沙盒环境中运行。html 的展示通过在 code.js 中调用如下代码实现:

    figma.showUI(__html__, { width: 450, height: 550, themeColors: true });
    
  • 插件代码的编译

    Figma 插件的 UI 代码需要编译成单个 html 文件,我们可以使用 vite 的 vite-plugin-singlefile 来实现。

    而 main 代码可以使用 esbuild,将 Typescript 代码编译成 JavaScript 代码。

    相关逻辑可以查看 TextColor2Android 的 plugin/package.json/scripts,获取具体的编译脚本细节

  • main 和 ui 进行通信

    发送的消息,默认支持 Javascript 的基本数据类型,非基本数据类型需要自行实现序列化和反序列化逻辑。

    ui 发送消息给 main:

    // ⚠️ 值得注意:
    // 1. 一定得是 pluginMessage 字段,不然 main 会接收不到
    // 2. messageContent 为发送消息的具体内容
    parent.postMessage({
      pluginMessage: messageContent
    }, "*")
    

    ui 接收 main 发送的消息:

    使用 window.addEventListener('message', messageHandler) 的方式实现,这里提供一个自定义的 Hook 供参考:

    // 如果只接收一次消息,则 oneShot 传入 true
    export const useEffectRegisterOnMessage = (callback: (message: any) => void, oneShot: boolean = false) => {
      const [flag, setFlag] = useState(true)
    
      useEffect(() => {
        const messageHandler = (event: MessageEvent) => {
          if (oneShot && !flag) return
    
          setFlag(false);
          const message = event.data.pluginMessage;
          if (message) {
            console.log("Recieve msg from Figma backend: ", message);
            callback(message);
          }
        }
    
        window.addEventListener('message', messageHandler);
    
        return () => {
          window.removeEventListener('message', messageHandler);
        }
      }, [callback]);
    };
    

    main 发送消息给 ui:

    figma.ui.postMessage(messageContent)
    

    main 接收 ui 发送的消息:

    figma.ui.onmessage = (msg) => {
      console.log("Recieve msg from UI: ", msg);
    };
    
  • 使用 on API 监听 Figma 内容的变化 ref

    // 支持 selection 和 current page 等
    figma.on("selectionchange", () => { 
    	console.log("selectionchange: ", figma.currentPage.selection) 
    })
    
  • 实现数据的持久化

    调用 figma.clientStorage API 即可,基于键值对存储,使用很方便。

    每个插件最多 1M 的持久化空间

    Ref:www.figma.com/plugin-docs…

  • 对 Figma 设计稿添加版本保存的 tag

    在执行一些操作之后,你可能需要在 Figma 的 History 留下一个版本号

    figma.saveVersionHistoryAsync("vtest");
    
  • 解决 UI 卡顿的问题

    由于 Javascript 的渲染是单线程模型,在操作 Figma 节点的时候,会抢占当前的 CPU 资源,导致 UI 刷新卡顿的情况。

    解决方式也很简单,分配给 UI 刷新一点时间片即可:

    async function function1(): Promise<Data> {
      await delay(155) // render UI
      // process Figma Node
    }
    
    function delay(time: number = 40) {
      return new Promise((resolve) => setTimeout(resolve, time));
    }
    

    参考:forum.figma.com/t/how-to-sp…

  • 添加 Figma 依赖

    最后,也是最重要的,就是添加 Figma 依赖了。

    在 package.json 中添加:

    "dependencies": {
      "@figma/plugin-typings": "^1.84.0"
    }
    

    因为 Figma 的 type 和其他的库不太一样,所以得在 tsconfig.json 中添加编译参数(参考 TextColor2Android 即可):

    "compilerOptions": {
      "typeRoots": ["./node_modules/@types", "./node_modules/@figma"]
    }
    

TextColor2Android 项目简介

github.com/sunnyswag/T…

  • 文件结构简介

    依稀记得自己拿到前端项目时,一脸懵逼的感觉,所以这里简单介绍一下前端项目的目录结构。以 TextColor2Android 为例,目录截图如下:

    Figma 插件开发入门指南在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Fi

    • pnpm-workspace.yaml:记录了该项目下,由 pnpm 管理的各个模块的路径信息(pnpm 是 npm 的优化版本,可以使用 npm 安装)
    • manifest.json:Figma 插件的配置文件
    • ./app 和 ./packages:各个模块的父目录
    • turbo.json:turbo 插件的配置信息,可以先不关注
    • pnpm-lock.yaml:使用 pnpm 进行包管理时,自动生成的文件,可以不用关注
  • 各模块简介

    Figma 插件开发入门指南在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Fi

    • model:存放公共的数据实体,几乎所有模块都会引用到
    • tsconfig:存放公共的 tsconfig.json,几乎所有模块都会引用到
    • gen-code:读取 Figma 节点内容,生成 Android 目标代码
    • view-model:消息分发层,接收 UI 的事件,实现消息的分发,更新 UI 实体,推送当前的 UI 实体给插件进行展示
    • plugin-ui:Figma 插件 UI 代码
    • debug:使用 next.js 驱动,调试 plugin-ui 模块
    • plugin:组装出整个插件运行逻辑,通过引用 plugin-ui 和 view-model 分别构建出 UI 逻辑和 Figma 沙盒逻辑
  • MVI(Model-View-Intent) 的 UI 开发模式简介

    Figma 插件开发入门指南在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Fi

    miro.medium.com/v2/resize:f…

    如上是 MVI 的示意图,简单来说,MVI 的基本工作流程:View 发送 Intent 给 ViewModel,ViewModel 处理完 Intent 之后,返回 State 给 View 进行渲染展示,UI 拿到的永远是最新最全的 State,所以可以规避很多 UI 渲染上的问题。

    在 TextColor2Android 中,MVI 是由 view-model 模块实现的。

    很明显,我们会用到策略模式来做消息分发,接收到对应类型的消息,执行对应的逻辑,如下简单介绍一下在 TextColor2Android 中 MVI 的实现细节。

    定义消息处理的接口:

    // iEventProcessor.ts
    export interface IEventProcessor {
      messageType: UIEventMsgType
      /**
       * 
       * @param event receive from UI
       * @returns wheather send message to notify ui or not,
       * default has no return value -> send msg
       */
      process: (event: PluginUIEvent) => Promise<boolean | void>
    }
    

    每个不同类型的消息,书写对应的实现,为了简单起见,更新 UI State 也放到了消息处理器里边(如下以 GetColorCode 这个 messageType 为例):

    // getColorCodeProcessor.ts
    export class GetColorCodeProcessor implements IEventProcessor {
      messageType = UIEventMsgType.GetColorCode;
    
      async process(event: PluginUIEvent) {
        const codeRes = genCode(event.colorCodeType)
        // 更新 UI State
        updateCurUIStateData({
          codeType: event.colorCodeType,
          code: codeRes
        })
      }
    }
    

    将每个消息处理的实体放到 map 集合中进行管理:

    // processorFactory.ts
    private static processors = [
      new CopyToClipboardProcessor(),
      new InitPluginProcessor(),
      new GetColorCodeProcessor(),
      // another msg processor
    ].reduce(
      (map, processor) => map.set(processor.messageType, processor),
      new Map<UIEventMsgType, IEventProcessor>
    )
    

    由统一的 factory 接收消息,操作 map 集合,实现消息的分发,消息处理完成,发送当前的 UI State 给 UI:

    // processorFactory.ts
    static async doProcess(event: PluginUIEvent) {
      const needNotifyUI = await ProcessorFactory.processors.get(event.msgType)?.process(event);
      if (needNotifyUI === undefined || needNotifyUI === true) {
        figma.ui.postMessage(curUIStateData)
      }
    }
    
  • 基于 TextColor2Android 进行二次开发

    如果你也有开发 Figma 插件的需要,可以考虑参照或者使用 TextColor2Android 为模板进行开发,具体操作为:

    • 新增/修改 view-model 模块的消息类型
    • 新增 model 模块的 UI State
    • 修改 plugin-ui 模块的 UI 逻辑
    • 新增其他相关的模块
    • 复用大部分 debug、plugin、tsconfig 模块的逻辑

最后

在实践现代化前端开发之前,我工作的大部分时间是做 Android 开发。本文是我在开发 Figma 插件时,所总结的一些学习路径及记录的经验总结。相信对想进行 Figma 插件开发,但是又无从下手的你,会有所帮助~ 同时,如果你对 TextColor2Android 插件感兴趣的话,可以点击 Reference 下的链接跳转到 Figma 插件主页查看详细信息。

Reference

github.com/bernaferrar…

TextColor2Android Github 链接

TextColor2Android Figma 插件

转载自:https://juejin.cn/post/7385776238491779109
评论
请登录