How to use Vue 3 to develop Figma plug-in? The following article will introduce you to the principle of Figma plug-in, record the process of developing Fimga plug-in using Vue 3, and attach the out-of-box code. I hope it will be helpful to you!
Use Vue 3 to develop Figma plug-in
Figma is a popular design tool nowadays, and more and more design teams Start switching from Sketch to Figma. The biggest feature of Figma is that it is developed using Web technology and is completely cross-platform. Figma plug-ins are also developed using Web technology. As long as you know html
, js
, css
, you can write a Figma plug-in.
Figma plug-in principle
Introduction to Figma architecture
Before introducing the Fimga plug-in, let’s first Learn about Fimga's technical architecture.
Figma is developed as a whole using React. The core canvas area is a piece of canvas
, which is rendered using WebGL. And the canvas engine part uses WebAssembly, which is why Figma can be so smooth. The desktop Figma App uses Electron - a framework for developing desktop applications using web technology. Electron is similar to a browser, but it actually runs a web application internally.
Figma plug-in principle
To develop a safe and reliable plug-in system on the Web, iframe
is undoubtedly the most direct solution. iframe
is a standard W3C specification that has been used in browsers for many years. Its characteristics are:
Security, natural sandbox isolation environment, pages within iframe Unable to operate the main frame;
Reliable, very good compatibility, and has been tested by the market for many years;
But it also has obvious shortcomings : Communication with the main framework can only be done through postMessage(STRING)
, and the communication efficiency is very low. If you want to operate a canvas element in the plug-in, you must first copy the node information of the element from the main frame to iframe
, and then update the node information to the main frame after completing the operation in iframe
. This involves a lot of communication, and for complex design draft node information is very huge, which may exceed the communication limit.
In order to ensure the ability to operate the canvas, you must return to the main thread. The main problem with plug-ins running on the main thread is security, so Figma developers implemented a js
sandbox environment on the main thread, using Realm API. Only pure js code and the API provided by Figma can be run in the sandbox, and browser APIs (such as network, storage, etc.) cannot be accessed, thus ensuring security.
Interested students are recommended to read "How to build a plugin system on the web and also sleep well at night" written by the official team, for details This article introduces the selection process of Figma plug-in solution, and I benefit a lot from reading it.
After comprehensive consideration, Figma divides the plug-in into two parts: the plug-in UI runs in iframe
, and the code that operates the canvas runs in the isolation sandbox of the main thread. The UI thread and the main thread communicate through postMessage
.
Plug-in configuration file manifest.json
is respectively configured with the main
field pointing to the js
file loaded into the main thread, ui
Field configuration is loaded into the html
file in iframe
. When opening the plug-in, the main thread calls the figma.showUI()
method to load the iframe
.
Write the simplest Figma plug-in
In order to understand the running process of the plug-in, we first write the simplest Figma plug-in. The function is simple: you can add or subtract square color blocks.
Install Figma Desktop
First you mustdownloadand install Figma Desktop.
Write the plug-in startup file manifest.json
Create a new code project and create a new manifest.json
file in the root directory with the following content:
{ "name": "simple-demo", "api": "1.0.0", "main": "main.js", "ui": "index.html", "editorType": [ "figjam", "figma" ] }
Write UI code
Create new root directoryindex.html
,
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Demo</title> <style> h1 { text-align: center; } p { color: red; } .buttons { margin-top: 20px; text-align: center; } .buttons button { width: 40px; } #block-num { font-size: 20px; } </style> </head> <body> <h1 id="Figma-nbsp-插件-nbsp-Demo">Figma 插件 Demo</h1> <p>当前色块数量:<span id="block-num">0</span></p> <div> <button id="btn-add" onclick="addBlock()">+</button> <button id="btn-sub" onclick="subBlock()">-</button> </div> <script> console.log('ui code runs!'); var blockNumEle = document.getElementById('block-num'); function addBlock() { console.log('add'); var num = +blockNumEle.innerText; num += 1; blockNumEle.innerText = num; } function subBlock() { console.log('substract'); var num = +blockNumEle.innerText; if (num === 0) return; num -= 1; blockNumEle.innerText = num; } </script> </body> </html>
Edit main js code
Create a new root directorymain.js
with the following content:
console.log('from code 2'); figma.showUI(__html__, { width: 400, height: 400, });
Start the plug-in
Figma desktop APP, right-click anywhere on the canvas Open the menu, Plugins
-> Development
-> Import plugin from manifest...
, select the manifest.json
created earlier file path to successfully import the plug-in.
Then right-click Plugins
-> Development
-> simple-demo
(plugin name) to open the plug-in.
Test click the button and it functions normally. It’s just that the color blocks don’t appear on the page yet (don’t worry).
The debugging console can be opened through Plugins
-> Development
-> Open console
. You can see the log we printed.
Operation canvas
As mentioned earlier, the canvas code runs on the main thread. For execution efficiency, the plug-in can only operate on the main thread if it wants to operate the canvas content. That is in main.js
. The top-level object figma
is exposed in main.js
, which encapsulates a series of APIs used to operate the canvas. For details, please see the official website documentation. We use figma.createRectangle()
to create a rectangle. The main thread needs to listen to events from the UI thread through figma.ui.onmessage
to respond. The modified code of main.js
is as follows:
console.log('figma plugin code runs!') figma.showUI(__html__, { width: 400, height: 400, }); const nodes = []; figma.ui.onmessage = (msg) => {= if (msg.type === "add-block") { const rect = figma.createRectangle(); rect.x = nodes.length * 150; rect.fills = [{ type: "SOLID", color: { r: 1, g: 0.5, b: 0 } }]; figma.currentPage.appendChild(rect); nodes.push(rect); } else if (msg.type === "sub-block") { const rect = nodes.pop(); if (rect) { rect.remove(); } } figma.viewport.scrollAndZoomIntoView(nodes); };
At the same time, some codes in index.html
need to be modified and passed to parent.postMessage
The main thread sends the event:
function addBlock() { console.log('add'); var num = +blockNumEle.innerText; num += 1; blockNumEle.innerText = num; parent.postMessage({ pluginMessage: { type: 'add-block' } }, '*') } function subBlock() { console.log('substract'); var num = +blockNumEle.innerText; if (num === 0) return; num -= 1; blockNumEle.innerText = num; parent.postMessage({ pluginMessage: { type: 'sub-block' } }, '*') }
Restart the plug-in, try it again, and find that you can successfully add and subtract color blocks.
Using Vue 3 to develop the Figma plug-in
Through the previous example, we have already understood the functions of the Figma plug-in Operating principle. But it is very inefficient to write code with this "native" js
and html
. We can use the latest Web technology to write code, as long as the packaged product includes a js
file running in the main frame and a html
file running in the iframe
That’s it. I decided to try using Vue 3
to develop plugins. (Learning video sharing: vuejs tutorial)
AboutVue 3 I won’t go into too much introduction. Everyone who knows understands it. If you don’t understand, you can go here first. Study it and come back. The focus here is not on which framework to use (change to vue 2, the react process is similar), but on the construction tools.
Vite Start a new project
Vite is a new generation build tool developed by the author of Vue, and is also the recommended build tool for Vue 3.
Let’s first build a Vue
TypeScript
template project.
npm init vite@latest figma-plugin-vue3 --template vue-ts cd figma-plugin-vue3 npm install npm run dev
Then open http://localhost:3000
through the browser to see the page.
Transplant the above demo code
We transplant the previous plug-in demo to Vue 3. src/App.vue
The code modification is as follows:
<script setup> import { ref } from 'vue'; const num = ref(0); console.log('ui code runs!'); function addBlock() { console.log('add'); num.value += 1; parent.postMessage({ pluginMessage: { type: 'add-block' } }, '*') } function subBlock() { console.log('substract'); if (num .value=== 0) return; num.value -= 1; parent.postMessage({ pluginMessage: { type: 'sub-block' } }, '*') } </script> <template> <h1 id="Figma-nbsp-插件-nbsp-Demo">Figma 插件 Demo</h1> <p>当前色块数量:<span id="block-num">{{ num }}</span></p> <div> <button id="btn-add" @click="addBlock">+</button> <button id="btn-sub" @click="subBlock">-</button> </div> </template> <style scoped> h1 { text-align: center; } p { color: red; } .buttons { margin-top: 20px; text-align: center; } .buttons button { width: 40px; } #block-num { font-size: 20px; } </style>
We store the js code running in the main thread sandbox in the src/worker
directory. Create a new src/worker/code.ts
with the following content:
console.log('figma plugin code runs!') figma.showUI(__html__, { width: 400, height: 400, }); const nodes: RectangleNode[] = []; figma.ui.onmessage = (msg) => { if (msg.type === "add-block") { const rect = figma.createRectangle(); rect.x = nodes.length * 150; rect.fills = [{ type: "SOLID", color: { r: 1, g: 0.5, b: 0 } }]; figma.currentPage.appendChild(rect); nodes.push(rect); } else if (msg.type === "sub-block") { const rect = nodes.pop(); if (rect) { rect.remove(); } } figma.viewport.scrollAndZoomIntoView(nodes); };
The ts type declaration of figma
is missing in the above code, so we need to install it.
npm i -D @figma/plugin-typings
Modify tsconfig.json
, add typeRoots
, so ts The code will not report errors. Also add "skipLibCheck": true
to resolve type declaration conflicts.
{ "compilerOptions": { // ... "skipLibCheck": true, "typeRoots": [ "./node_modules/@types", "./node_modules/@figma" ] }, }
Modify build configuration
The build products required by the Figma plug-in are:
##manifest.json
File as plug-in configuration
index.html
as UI code
code.js
As the main thread js code
Add the manifest.json file in the public directory
public All files in the directory will be Go to the build product
dist directory.
{ "name": "figma-plugin-vue3", "api": "1.0.0", "main": "code.js", "ui": "index.html", "editorType": [ "figjam", "figma" ] }
vite.config.ts 中增加构建入口
默认情况下 vite
会用 index.html
作为构建入口,里面用到的资源会被打包构建。我们还需要一个入口,用来构建主线程 js 代码。
执行 npm i -D @types/node
,安装 Node.js
的类型声明,以便在 ts 中使用 Node.js
API。 vite.config.ts
的 build.rollupOptions
中增加 input
。默认情况下输出产物会带上文件 hash
,所以也要修改 output
配置:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], build: { sourcemap: 'inline', rollupOptions: { input:{ main: resolve(__dirname, 'index.html'), code: resolve(__dirname, 'src/worker/code.ts'), }, output: { entryFileNames: '[name].js', }, }, }, })
运行构建
执行 npm run build
, dist
目录会有构建产物。然后我们按照前面的步骤,将 dist
目录添加为 Figma 插件。 Plugins
-> Development
-> Import plugin from manifest...
,选择 dist/manifest.json
文件路径。
启动插件......怎么插件里一片空白?好在 Figma 里面有 devtools 调试工具,我们打开瞧一瞧。
可以看到,我们的 index.html
已经成功加载,但是 js 代码没加载所以页面空白。js、css 等资源是通过相对路径引用的,而我们的 iframe
中的 src
是一个 base64
格式内容,在寻找 js 资源的时候因为没有域名,所以找不到资源。
解决办法也很简单,我们给资源加上域名,然后本地起一个静态资源服务器就行了。修改 vite.config.ts
,加上 base: 'http://127.0.0.1:3000'
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], base: 'http://127.0.0.1:3000', build: { sourcemap: 'inline', rollupOptions: { input: { main: resolve(__dirname, 'index.html'), code: resolve(__dirname, 'src/worker/code.ts'), }, output: { entryFileNames: '[name].js', }, }, }, preview: { port: 3000, }, })
重新构建代码 npm run build
。然后启动静态资源服务器 npm run preview
。通过浏览器访问 http://localhost:3000/
可以看到内容。
然后重新打开 Figma 插件看看。果然,插件已经正常了!
Figma 加载插件只需要
index.html
和code.js
,其他资源都可以通过网络加载。这意味着我们可以将 js、css 资源放在服务端,实现插件的热更?不知道发布插件的时候会不会有限制,这个我还没试过。
开发模式
我们已经能成功通过 Vue 3 来构建 Figma 插件了,但是我不想每次修改代码都要构建一遍,我们需要能够自动构建代码的开发模式。
vite 自动的 dev 模式是启动了一个服务,没有构建产物(而且没有类似webpack里面的 writeToDisk
配置),所以无法使用。
watch 模式
vite
的 build 命令有watch模式,可以监听文件改动然后自动执行 build
。我们只需要修改 package.json
, scripts
里新增 "watch": "vite build --watch"
。
npm run watch # 同时要在另一个终端里启动静态文件服务 npm run preview
这种方式虽然修改代码后会自动编译,但是每次还是要关闭插件并重新打开才能看到更新。这样写UI还是太低效了,能不能在插件里实现 HMR
(模块热重载)功能呢?
dev 模式
vite dev 的问题在于没有构建产物。 code.js
是运行在 Fimga 主线程沙箱中的,这部分是无法热重载的,所以可以利用 vite build --watch
实现来编译。需要热重载的是 index.html
以及相应的 js 、css 资源。
先来看一下 npm run dev
模式下的 html 资源有什么内容:
理论上来说,我们只需要把这个 html 手动写入到 dist
目录就行,热重载的时候 html 文件不需要修改。直接写入的话会遇到资源是相对路径的问题,所以要么把资源路径都加上域名( http://localhost:3000
),或者使用 <base>标签。
手动生成 html 文件
对比上面的 html 代码和根目录的 index.html
文件,发现只是增加了一个 <script type="module" src="/@vite/client"></script>
。所以我们可以自己解析 index.html
,然后插入相应这个标签,以及一个 <base>
标签。解析 HTML 我们用 jsdom
。
const JSDOM = require('jsdom'); const fs = require('fs'); // 生成 html 文件 function genIndexHtml(sourceHTMLPath, targetHTMLPath) { const htmlContent = fs.readFileSync(sourceHTMLPath, 'utf-8'); const dom = new JSDOM(htmlContent); const { document } = dom.window; const script = document.createElement('script'); script.setAttribute('type', 'module'); script.setAttribute('src', '/@vite/client'); dom.window.document.head.insertBefore(script, document.head.firstChild); const base = document.createElement('base'); base.setAttribute('href', 'http://127.0.0.1:3000/'); dom.window.document.head.insertBefore(base, document.head.firstChild); const result = dom.serialize(); fs.writeFileSync(targetHTMLPath, result); }
同时 vite 提供了 JavaScript API,所以我们可以代码组织起来,写一个 js 脚本来启动开发模式。新建文件 scripts/dev.js
,完整内容如下:
const { JSDOM } = require('jsdom'); const fs = require('fs'); const path = require('path'); const vite = require('vite'); const rootDir = path.resolve(__dirname, '../'); function dev() { const htmlPath = path.resolve(rootDir, 'index.html'); const targetHTMLPath = path.resolve(rootDir, 'dist/index.html'); genIndexHtml(htmlPath, targetHTMLPath); buildMainCode(); startDevServer(); } // 生成 html 文件 function genIndexHtml(sourceHTMLPath, targetHTMLPath) { const htmlContent = fs.readFileSync(sourceHTMLPath, 'utf-8'); const dom = new JSDOM(htmlContent); const { document } = dom.window; const script = document.createElement('script'); script.setAttribute('type', 'module'); script.setAttribute('src', '/@vite/client'); dom.window.document.head.insertBefore(script, document.head.firstChild); const base = document.createElement('base'); base.setAttribute('href', 'http://127.0.0.1:3000/'); dom.window.document.head.insertBefore(base, document.head.firstChild); const result = dom.serialize(); fs.writeFileSync(targetHTMLPath, result); } // 构建 code.js 入口 async function buildMainCode() { const config = vite.defineConfig({ configFile: false, // 关闭默认使用的配置文件 build: { emptyOutDir: false, // 不要清空 dist 目录 lib: { // 使用库模式构建 entry: path.resolve(rootDir, 'src/worker/code.ts'), name: 'code', formats: ['es'], fileName: (format) => `code.js`, }, sourcemap: 'inline', watch: {}, }, }); return vite.build(config); } // 开启 devServer async function startDevServer() { const config = vite.defineConfig({ configFile: path.resolve(rootDir, 'vite.config.ts'), root: rootDir, server: { hmr: { host: '127.0.0.1', // 必须加上这个,否则 HMR 会报错 }, port: 3000, }, build: { emptyOutDir: false, // 不要清空 dist 目录 watch: {}, // 使用 watch 模式 } }); const server = await vite.createServer(config); await server.listen() server.printUrls() } dev();
执行 node scripts/dev.js
,然后在 Figma 中重启插件。试试修改一下 Vue 代码,发现插件内容自动更新了!
最后在 package.json
中新建一个修改一下dev的内容为 "dev": "node scripts/dev.js"
就可以了。
通过请求来获取 HTML
前面通过自己生产 index.html
的方式有很大的弊端:万一后续 vite 更新后修改了默认 html 的内容,那我们的脚本也要跟着修改。有没有更健壮的方式呢?我想到可以通过请求 devServer
来获取 html 内容,然后写入本地。话不多说,修改后代码如下:
const { JSDOM } = require('jsdom'); const fs = require('fs'); const path = require('path'); const vite = require('vite'); const axios = require('axios'); const rootDir = path.resolve(__dirname, '../'); async function dev() { // const htmlPath = path.resolve(rootDir, 'index.html'); const targetHTMLPath = path.resolve(rootDir, 'dist/index.html'); await buildMainCode(); await startDevServer(); // 必须放到 startDevServer 后面执行 await genIndexHtml(targetHTMLPath); } // 生成 html 文件 async function genIndexHtml(/* sourceHTMLPath,*/ targetHTMLPath) { const htmlContent = await getHTMLfromDevServer(); const dom = new JSDOM(htmlContent); // ... const result = dom.serialize(); fs.writeFileSync(targetHTMLPath, result); } // ... // 通过请求 devServer 获取HTML async function getHTMLfromDevServer () { const rsp = await axios.get('http://localhost:3000/index.html'); return rsp.data; } dev();
结语
Figma 基于Web平台的特性使之能成为真正跨平台的设计工具,只要有浏览器就能使用。同时也使得开发插件变得非常简单,非专业人士经过简单的学习也可以上手开发一个插件。而Web社区有数量庞大的开发者,相信 Figma 的插件市场也会越来越繁荣。
本文通过一个例子,详细讲述了使用 Vue 3 开发 Figma 插件的过程,并且完美解决了开发模式下热重载的问题。我将模板代码提交到了 Git 仓库中,需要的同学可以直接下载使用:figma-plugin-vue3。
开发 Figma 插件还会遇到一些其他问题,例如如何进行网络请求、本地存储等,有空再继续分享我的实践心得。
本文转载自:https://juejin.cn/post/7084639146915921956
作者:大料园
(学习视频分享:web前端开发)
The above is the detailed content of Record a process of developing Fimga plug-in using Vue 3. For more information, please follow other related articles on the PHP Chinese website!

vue中props可以传递函数;vue中可以将字符串、数组、数字和对象作为props传递,props主要用于组件的传值,目的为了接收外面传过来的数据,语法为“export default {methods: {myFunction() {// ...}}};”。

本篇文章带大家聊聊vue指令中的修饰符,对比一下vue中的指令修饰符和dom事件中的event对象,介绍一下常用的事件修饰符,希望对大家有所帮助!

如何覆盖组件库样式?下面本篇文章给大家介绍一下React和Vue项目中优雅地覆盖组件库样式的方法,希望对大家有所帮助!

react与vue的虚拟dom没有区别;react和vue的虚拟dom都是用js对象来模拟真实DOM,用虚拟DOM的diff来最小化更新真实DOM,可以减小不必要的性能损耗,按颗粒度分为不同的类型比较同层级dom节点,进行增、删、移的操作。


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

Zend Studio 13.0.1
Powerful PHP integrated development environment

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function

Dreamweaver Mac version
Visual web development tools

Atom editor mac version download
The most popular open source editor

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),
