新聞中心
本篇內(nèi)容介紹了“怎么使用Vue3開發(fā)Fimga插件”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
成都創(chuàng)新互聯(lián)成都企業(yè)網(wǎng)站建設(shè)服務(wù),提供網(wǎng)站設(shè)計制作、做網(wǎng)站網(wǎng)站開發(fā),網(wǎng)站定制,建網(wǎng)站,網(wǎng)站搭建,網(wǎng)站設(shè)計,響應(yīng)式網(wǎng)站建設(shè),網(wǎng)頁設(shè)計師打造企業(yè)風(fēng)格網(wǎng)站,提供周到的售前咨詢和貼心的售后服務(wù)。歡迎咨詢做網(wǎng)站需要多少錢:18982081108
用 Vue 3 開發(fā) Figma 插件
Figma 是一款當(dāng)下流行的設(shè)計工具,越來越多的設(shè)計團隊開始從 Sketch 轉(zhuǎn)向 Figma。Figma 最大的特點是使用Web技術(shù)開發(fā),實現(xiàn)了完全的跨平臺。 Figma 插件也是使用 Web 技術(shù)開發(fā),只要會 html
、 js
、 css
就能動手寫一個 Figma 插件。
Figma 插件原理
Figma 架構(gòu)簡介
介紹 Fimga 插件之前,我們先來了解一下 Fimga 的技術(shù)架構(gòu)。
Figma 整體是用 React 開發(fā)的,核心的畫布區(qū)是一塊 canvas
,使用WebGL來渲染。并且畫布引擎部分使用的是WebAssembly,這就是 Figma 能夠如此流暢的原因。桌面端的Figma App 使用了 Electron——一個使用Web技術(shù)開發(fā)桌面應(yīng)用的框架。Electron 類似于一個瀏覽器,內(nèi)部運行的其實還是一個Web應(yīng)用。
Figma 插件原理
在Web端開發(fā)一套安全、可靠的插件系統(tǒng), iframe
無疑是最直接的方案。 iframe
是標準的W3C規(guī)范,在瀏覽器上已經(jīng)經(jīng)過多年應(yīng)用,它的特點是:
安全,天然沙箱隔離環(huán)境,iframe內(nèi)頁面無法操作主框架;
可靠,兼容性非常好,且經(jīng)過了多年市場的檢驗;
但是它也有明顯的缺點:與主框架通信只能通過 postMessage(STRING)
的方式,通信效率非常低。如果要在插件里操作一個畫布元素,首先要將元素的節(jié)點信息從主框架拷貝到 iframe
中,然后在 iframe
中操作完再更新節(jié)點信息給主框架。這涉及到大量通信,而且對于復(fù)雜的設(shè)計稿節(jié)點信息是非常巨大的,可能超過通信的限制。
為了保證操作畫布的能力,必須回到主線程。插件在主線程運行的問題主要在于安全性上,于是Figma的開發(fā)人員在主線程實現(xiàn)了一個 js
沙箱環(huán)境,使用了Realm API。沙箱中只能運行純粹的 js 代碼和Figma提供的API,無法訪問瀏覽器API(例如網(wǎng)絡(luò)、存儲等),這樣就保證了安全性。
感興趣的同學(xué)推薦閱讀官方團隊寫的《How to build a plugin system on the web and also sleep well at night》,詳細介紹了 Figma 插件方案的選擇過程,讀來獲益良多。
經(jīng)過綜合考慮,F(xiàn)igma 將插件分成兩個部分:插件UI運行在 iframe
中,操作畫布的代碼運行在主線程的隔離沙箱中。UI線程和主線程通過 postMessage
通信。
插件配置文件 manifest.json
中分別配置 main
字段指向加載到主線程的 js
文件, ui
字段配置加載到 iframe
中的 html
文件。打開插件時,主線程調(diào)用 figma.showUI()
方法加載 iframe
。
寫一個最簡單的 Figma 插件
為了了解插件的運行過程,我們先寫一個最簡單的 Figma 插件。功能很簡單:可以加減正方形色塊。
安裝Figma桌面端
首先要下載并安裝好 Figma 桌面端。
編寫插件的啟動文件 manifest.json
新建一個代碼工程,在根目錄中新建 manifest.json
文件,內(nèi)容如下:
{ "name": "simple-demo", "api": "1.0.0", "main": "main.js", "ui": "index.html", "editorType": [ "figjam", "figma" ] }
編寫UI代碼
根目錄新建 index.html
,
Demo Figma 插件 Demo
當(dāng)前色塊數(shù)量:0
編輯main js 代碼
根目錄新建 main.js
,內(nèi)容如下:
console.log('from code 2'); figma.showUI(__html__, { width: 400, height: 400, });
啟動插件
Figma桌面APP,畫布任意地方右鍵打開菜單, Plugins
-> Development
-> Import plugin from manifest...
,選擇前面創(chuàng)建的 manifest.json
文件路徑,即可成功導(dǎo)入插件。
然后通過右鍵, Plugins
-> Development
-> simple-demo
(插件名),就可以打開插件。
測試點擊按鈕,功能正常。只不過頁面上還未出現(xiàn)色塊(別著急)。
通過 Plugins
-> Development
-> Open console
可以打開調(diào)試控制臺。可以看到我們打印的日志。
操作畫布
前面講了,畫布代碼是運行在主線程的,為了執(zhí)行效率,插件要操作畫布內(nèi)容也只能在主線程執(zhí)行,即在 main.js
中。 main.js
中暴露了頂級對象 figma
,封裝了用來操作畫布的一系列API,具體可以去看官網(wǎng)文檔。我們用 figma.createRectangle()
來創(chuàng)建一個矩形。主線程需要通過 figma.ui.onmessage
監(jiān)聽來自UI線程的事件,從而做出響應(yīng)。修改后的 main.js
代碼如下:
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); };
同時要修改 index.html
中的部分代碼,通過 parent.postMessage
給主線程發(fā)送事件:
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' } }, '*') }
重新啟動插件,再試驗一下,發(fā)現(xiàn)已經(jīng)可以成功加減色塊了。
使用 Vue 3 開發(fā) Figma 插件
通過前面的例子,我們已經(jīng)清楚 Figma 插件的運行原理。但是用這種“原生”的 js
、 html
來編寫代碼非常低效的。我們完全可以用最新的Web技術(shù)來編寫代碼,只要打包產(chǎn)物包括一個運行在主框架的 js
文件和一個給 iframe
運行的 html
文件即可。我決定嘗試使用 Vue 3
來開發(fā)插件。(學(xué)習(xí)視頻分享:vuejs教程)
關(guān)于 Vue 3 就不多做介紹了,懂的都懂,不懂的看到這里可以先去學(xué)習(xí)一下再來。這里的重點不在于用什么框架(改成用vue 2、react過程也差不多),而在于構(gòu)建工具。
Vite 啟動一個新項目
Vite 是Vue的作者開發(fā)的新一代構(gòu)建工具,也是 Vue 3推薦的構(gòu)建工具。
我們先建一個 Vue
+ TypeScript
的模板項目。
npm init vite@latest figma-plugin-vue3 --template vue-ts cd figma-plugin-vue3 npm install npm run dev
然后通過瀏覽器打開 http://localhost:3000
就能看到頁面。
移植上述demo代碼
我們把前面的插件demo移植到 Vue 3 中。 src/App.vue
代碼修改如下:
Figma 插件 Demo
當(dāng)前色塊數(shù)量:{{ num }}
我們在 src/worker
目錄存放運行在主線程沙箱中的js代碼。新建 src/worker/code.ts
,內(nèi)容如下:
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); };
上述代碼中缺少 figma
的 ts 類型聲明,所以我們需要安裝一下。
npm i -D @figma/plugin-typings
修改 tsconfig.json
,添加 typeRoots
,這樣 ts 代碼就不會報錯了。同時也要加上 "skipLibCheck": true
,解決類型聲明沖突問題。
{ "compilerOptions": { // ... "skipLibCheck": true, "typeRoots": [ "./node_modules/@types", "./node_modules/@figma" ] }, }
修改構(gòu)建配置
Figma 插件需要的構(gòu)建產(chǎn)物有:
manifest.json
文件作為插件配置index.html
作為UI代碼code.js
作為主線程js代碼
在 public 目錄中添加 manifest.json 文件
public
目錄中的文件都會負責(zé)到構(gòu)建產(chǎn)物 dist
目錄下。
{ "name": "figma-plugin-vue3", "api": "1.0.0", "main": "code.js", "ui": "index.html", "editorType": [ "figjam", "figma" ] }
vite.config.ts 中增加構(gòu)建入口
默認情況下 vite
會用 index.html
作為構(gòu)建入口,里面用到的資源會被打包構(gòu)建。我們還需要一個入口,用來構(gòu)建主線程 js 代碼。
執(zhí)行 npm i -D @types/node
,安裝 Node.js
的類型聲明,以便在 ts 中使用 Node.js
API。 vite.config.ts
的 build.rollupOptions
中增加 input
。默認情況下輸出產(chǎn)物會帶上文件 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', }, }, }, })
運行構(gòu)建
執(zhí)行 npm run build
, dist
目錄會有構(gòu)建產(chǎn)物。然后我們按照前面的步驟,將 dist
目錄添加為 Figma 插件。 Plugins
-> Development
-> Import plugin from manifest...
,選擇 dist/manifest.json
文件路徑。
啟動插件......怎么插件里一片空白?好在 Figma 里面有 devtools 調(diào)試工具,我們打開瞧一瞧。
可以看到,我們的 index.html
已經(jīng)成功加載,但是 js 代碼沒加載所以頁面空白。js、css 等資源是通過相對路徑引用的,而我們的 iframe
中的 src
是一個 base64
格式內(nèi)容,在尋找 js 資源的時候因為沒有域名,所以找不到資源。
解決辦法也很簡單,我們給資源加上域名,然后本地起一個靜態(tài)資源服務(wù)器就行了。修改 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, }, })
重新構(gòu)建代碼 npm run build
。然后啟動靜態(tài)資源服務(wù)器 npm run preview
。通過瀏覽器訪問 http://localhost:3000/
可以看到內(nèi)容。
然后重新打開 Figma 插件看看。果然,插件已經(jīng)正常了!
Figma 加載插件只需要
index.html
和code.js
,其他資源都可以通過網(wǎng)絡(luò)加載。這意味著我們可以將 js、css 資源放在服務(wù)端,實現(xiàn)插件的熱更?不知道發(fā)布插件的時候會不會有限制,這個我還沒試過。
開發(fā)模式
我們已經(jīng)能成功通過 Vue 3 來構(gòu)建 Figma 插件了,但是我不想每次修改代碼都要構(gòu)建一遍,我們需要能夠自動構(gòu)建代碼的開發(fā)模式。
vite 自動的 dev模式是啟動了一個服務(wù),沒有構(gòu)建產(chǎn)物(而且沒有類似webpack里面的 writeToDisk
配置),所以無法使用。
watch 模式
vite
的 build 命令有watch模式,可以監(jiān)聽文件改動然后自動執(zhí)行 build
。我們只需要修改 package.json
, scripts
里新增 "watch": "vite build --watch"
。
npm run watch # 同時要在另一個終端里啟動靜態(tài)文件服務(wù) npm run preview
這種方式雖然修改代碼后會自動編譯,但是每次還是要關(guān)閉插件并重新打開才能看到更新。這樣寫UI還是太低效了,能不能在插件里實現(xiàn) HMR
(模塊熱重載)功能呢?
dev 模式
vite dev 的問題在于沒有構(gòu)建產(chǎn)物。 code.js
是運行在 Fimga 主線程沙箱中的,這部分是無法熱重載的,所以可以利用 vite build --watch
實現(xiàn)來編譯。需要熱重載的是 index.html
以及相應(yīng)的 js 、css 資源。
先來看一下 npm run dev
模式下的 html 資源有什么內(nèi)容:
理論上來說,我們只需要把這個 html 手動寫入到 dist
目錄就行,熱重載的時候 html 文件不需要修改。直接寫入的話會遇到資源是相對路徑的問題,所以要么把資源路徑都加上域名( http://localhost:3000
),或者使用
手動生成 html 文件
對比上面的 html 代碼和根目錄的 index.html
文件,發(fā)現(xiàn)只是增加了一個 。所以我們可以自己解析
index.html
,然后插入相應(yīng)這個標簽,以及一個
標簽。解析 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 腳本來啟動開發(fā)模式。新建文件 scripts/dev.js
,完整內(nèi)容如下:
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); } // 構(gòu)建 code.js 入口 async function buildMainCode() { const config = vite.defineConfig({ configFile: false, // 關(guān)閉默認使用的配置文件 build: { emptyOutDir: false, // 不要清空 dist 目錄 lib: { // 使用庫模式構(gòu)建 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();
執(zhí)行 node scripts/dev.js
,然后在 Figma 中重啟插件。試試修改一下 Vue 代碼,發(fā)現(xiàn)插件內(nèi)容自動更新了!
最后在 package.json
中新建一個修改一下dev的內(nèi)容為 "dev": "node scripts/dev.js"
就可以了。
通過請求來獲取 HTML
前面通過自己生產(chǎn) index.html
的方式有很大的弊端:萬一后續(xù) vite 更新后修改了默認 html 的內(nèi)容,那我們的腳本也要跟著修改。有沒有更健壯的方式呢?我想到可以通過請求 devServer
來獲取 html 內(nèi)容,然后寫入本地。話不多說,修改后代碼如下:
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 后面執(zhí)行 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();
“怎么使用Vue3開發(fā)Fimga插件”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
本文標題:怎么使用Vue3開發(fā)Fimga插件
網(wǎng)址分享:http://www.ef60e0e.cn/article/pedgph.html