基于Jenkins、Figma插件實現(xiàn)原理探究web系統(tǒng)插件化架構(gòu)

背景:對于大型web應(yīng)用而言,功能極其豐富復(fù)雜,為了具備擴展性,部分項目選擇插件化架構(gòu)方式,開放一部分系統(tǒng)Hook給具備開發(fā)能力的用戶,不但提升用戶的體驗感,還同時豐富平臺功能,一舉兩得。如何構(gòu)建具備插件化能力的平臺?本文嘗試通過分析jenkins jar包插件實現(xiàn)方式及Figma前端插件實現(xiàn)方式探究插件化架構(gòu)方案。

我們常見的支持插件化的應(yīng)用是一些桌面端編輯器,如VSCode,Eclipse,Idea,Sublime等,也有支持動態(tài)擴展的web應(yīng)用,如構(gòu)建工具Jenkins,支持前端插件擴展的web應(yīng)用,如設(shè)計工具Figma。不管哪種應(yīng)用方式,基本的設(shè)計邏輯大致如下。

插件系統(tǒng)通用方案.png

我們當前主要研究的是web系統(tǒng)的插件化構(gòu)架方案,本地插件化軟件先不討論。主要以jenkins和figma的兩種實現(xiàn)方式進行探討。

Jenkins插件化系統(tǒng)

10.png

Jenkins可以支持git, svn, maven等很多功能,這些都是Jenkins的插件,Jenkins通過擴展點及前端視圖模板來提供插件擴展能力,以jar包的方式上傳到指定目錄,創(chuàng)建類加載器 class-loader,使用插件策略PluginStrategy加載可以激活的插件。

(一)擴展點

Jenkins有很多的擴展點ExtensitonPoint),它是Jenkins系統(tǒng)的某個方面的接口或抽象類。這些接口定義了需要實現(xiàn)的方法,而Jenkins插件需要實現(xiàn)這些方法,也可以叫做在此擴展點之上進行擴展Jenkins。有關(guān)擴展點的詳細信息,請參閱Jenkins 官方ExtentionPoints文檔。通過這些擴展點我們可以寫插件來實現(xiàn)自己的需求。
下面是一些常用的擴展點:

  • Scm :代表源碼管理的一個步驟,如下面的Git,Subversion就是擴展的Scm
11.png
  • Builder : 代表構(gòu)建的一個步驟,如下圖中在構(gòu)建過程中,我們可以增加一個構(gòu)建步驟,而每一個選項都是對應(yīng)一個Builder,在每一個Builder中都有自己不同的功能。如Execute shell,這就是一個ShellBuilder,意味著在構(gòu)建過程中會執(zhí)行一個shell命令

    12.png

  • Trigger:代表一個構(gòu)建的觸發(fā),當滿足一個什么樣的條件時觸發(fā)這個項目開始構(gòu)建。比較常用的觸發(fā)就是當代碼變更時觸發(fā),如果我們需要實現(xiàn)一些比較復(fù)雜的觸發(fā)邏輯,就需要擴展Trigger這個擴展點

13.png
  • Publisher:Publisher代表一個項目構(gòu)建完成后需要執(zhí)行的步驟,如選項中的E-Mail Notifaction就是一個Publisher插件,選擇這個選項后,當項目構(gòu)建完成,就會使用email來通知用戶,假如想要在項目構(gòu)建完成后將構(gòu)建目標產(chǎn)物發(fā)送到服務(wù)器上,則可以擴展此擴展點。
14.png
(二)Jenkins中的視圖

Jenkins 使用jelly來編寫視圖,Jelly 是一種基于 Java 技術(shù)和 XML 的腳本編制和處理引擎。Jelly 的特點是有許多基于 JSTL (JSP 標準標記庫,JSP Standard Tag Library)、Ant、Velocity 及其它眾多工具的可執(zhí)行標記。Jelly 還支持 Jexl(Java 表達式語言,Java Expression Language),Jexl 是 JSTL 表達式語言的擴展版本。Jenkins的界面繪制就是通過Jelly實現(xiàn)的。

另外一個開源的插件化后臺管理系統(tǒng):grape: 前后端可插件開發(fā)的后臺管理系統(tǒng) (gitee.com)

Figma前端插件系統(tǒng)

Figma 是一個在線協(xié)作式 UI 設(shè)計工具,具有插件擴展功能,只要有前端開發(fā)能力的用戶均可開發(fā)自己的插件來擴展設(shè)計體驗。

image-20210611163806313.png

Figma的插件是純前端的插件方式,沒有后端代碼,它的插件系統(tǒng)是如何工作的?

這是一個基于 TypeScript + React 技術(shù)棧,使用 Webpack 構(gòu)建的 Figma 插件目錄結(jié)構(gòu)如下:

├── README.md
├── figma.d.ts
├── manifest.json
├── package-lock.json
├── package.json
├── src
│   ├── code.ts
│   ├── logo.svg
│   ├── ui.css
│   ├── ui.html
│   └── ui.tsx
├── tsconfig.json
└── webpack.config.js

在其 manifest.json 文件中包含了一些簡單的信息。

{  
"name": "React Sample",  
"id": "738168449509241862",  
"api": "1.0.0",  
"main": "dist/code.js",  
"ui": "dist/ui.html"
}

ui展示是通過如下代碼加載,會彈出個DIV,展示manifest.josn中指定的ui地址內(nèi)容:

figma.showUI(__html__);

可以看出 Figma 將插件入口分為了 mainui 兩部分, main 中包含了插件實際運行時的邏輯,而 ui 則是一個插件的 HTML 片段。即 UI 與邏輯分離。 main 中的 js 文件被包裹在一個 iframe 里加載到頁面上。而 ui 中的 HTML 最終也被包裹在一個 iframe 里渲染出來。

為什么這么要用iframe包裹?

  1. 首先是安全性考慮
    iframe,一個瀏覽器自帶的沙箱環(huán)境。將插件代碼由 iframe 包裹起來,由于 iframe 天然的限制,這將確保插件代碼無法操作 Figma 主界面上下文,同時也可以只開放一份白名單 API 供插件調(diào)用。
    iframe參考
  2. 其次是避免樣式污染
    這將有效的避免插件 UI 層 CSS 代碼導(dǎo)致全局樣式污染,使主程序與插件樣式相互獨立。

插件如何與主程序通信?
在上一層使用 window.addEventListener進行監(jiān)控,事件通信使用 parent.postMessage,發(fā)送事件及數(shù)據(jù)。

Inner Plugin Iframe:

document.getElementById('create').onclick = () => {
        const textbox = document.getElementById('count');
        const count = parseInt(textbox.value, 10);
        parent.postMessage({ pluginMessage: { type: 'create-rectangles', count } }, '*')
    }
                
document.getElementById('cancel').onclick = () => {
        parent.postMessage({ pluginMessage: { type: 'cancel' } }, '*')
    }

Shim Plugin Iframe:

var messageHandler = (event) => {
        var pluginIframeElement = document.getElementById("plugin-iframe")
        if (pluginIframeElement && event.source === pluginIframeElement.contentWindow) {
            parent.postMessage({ origin: event.origin, data: event.data }, window.location.origin)
        }
    }
    window.addEventListener("message", messageHandler)
    window.__FIGMA_PLUGIN_SANDBOX_PAGE_LOADED = true
            
        

整體架構(gòu)圖描述,大致如下:

Figma插件系統(tǒng).png

開發(fā)的插件可在本地app中進行調(diào)試,最終發(fā)布到服務(wù)器。

image-20210611171346786.png

總結(jié)

插件系統(tǒng)在設(shè)計時要考慮的基本內(nèi)容,如何開放數(shù)據(jù)接口,如何加載插件,何時何地啟動插件,插件如何與主程序通信問題,如何保證插件安全性?前端插件化使用iframe sandbox是一個通用可行的辦法,但依然會有很多問題。

參考文檔

How Plugins Run · Figma Developers

Figma 插件開發(fā) 101

大型 Web 應(yīng)用插件化架構(gòu)探索

iframe參考

window.postMessage

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容