掘金2023年度人氣創(chuàng)作者打榜中,快來(lái)幫我打榜吧~ https://activity.juejin.cn/rank/2023/writer/747323639208391?utm_campaign=annual_2023&utm_medium=self_web_share&utm_source=MiyueFE
前言
首先需要明確的一點(diǎn)是,本文的出發(fā)點(diǎn) 純粹是針對(duì)工作流開(kāi)發(fā) 的場(chǎng)景的選型對(duì)比,其他業(yè)務(wù)場(chǎng)景下建議重新調(diào)研。
什么是工作流?
工作流,即 Workflow,是對(duì)工作流程及其各操作步驟之間業(yè)務(wù)規(guī)則的抽象、概括描述。工作流建模,即將工作流程中的工作如何前后組織在一起的邏輯和規(guī)則,在計(jì)算機(jī)中以恰當(dāng)?shù)哪P捅磉_(dá)并對(duì)其實(shí)施計(jì)算。工作流要解決的主要問(wèn)題是:為實(shí)現(xiàn)某個(gè)業(yè)務(wù)目標(biāo),利用計(jì)算機(jī)在多個(gè)參與者之間按某種預(yù)定規(guī)則自動(dòng)傳遞文檔、信息或者任務(wù)。
以上是維基百科對(duì) 工作流技術(shù) 的定義。之所以稱(chēng)為 技術(shù),是因?yàn)槠渲邪?業(yè)務(wù)規(guī)則抽象與規(guī)則模型的創(chuàng)建,并可以將其用來(lái)實(shí)現(xiàn)一系列自動(dòng)化操作。通常來(lái)說(shuō),一個(gè)工作流的定義,包含至少一個(gè) 觸發(fā)點(diǎn) 與一個(gè) 終止點(diǎn),當(dāng)被觸發(fā)后會(huì)由觸發(fā)點(diǎn)沿著定義的路徑和條件自動(dòng)流轉(zhuǎn)直到進(jìn)入終止點(diǎn)。
所以工作流常常會(huì)與 規(guī)則引擎 結(jié)合使用。
而為了使得業(yè)務(wù)人員也能快速理解和創(chuàng)建一套符合業(yè)務(wù)規(guī)范的工作流,最后由 OMG 發(fā)布了 BPMN(全稱(chēng) Business Process Model and Notation,業(yè)務(wù)流程模型和標(biāo)記法)規(guī)范,用來(lái)標(biāo)準(zhǔn)化和約束對(duì)工作流的定義,并實(shí)現(xiàn)業(yè)務(wù)人員與實(shí)際程序的解耦,兩者由 XML 進(jìn)行信息交互。
既然有了工作流,那么就需要后端的對(duì)應(yīng)的 工作流引擎 了。
什么是工作流引擎?
既然定義了一套工作流,那么就需要對(duì)應(yīng)的后臺(tái)服務(wù)來(lái)解析和運(yùn)行這一套工作流定義。所以工作流引擎其實(shí)就是 一套用來(lái)解析工作流定義并根據(jù)根據(jù)定義驅(qū)動(dòng)工作流正常執(zhí)行和流轉(zhuǎn)的系統(tǒng)。
就目前來(lái)說(shuō),常用的一般有 Camunda、Activiti、Flowable、Jbpm
,也有一些國(guó)內(nèi)自研的工作流平臺(tái)。不過(guò)目前仍然以最前面的三個(gè)使用更為廣泛。
以這三個(gè)平臺(tái)為例,它們?cè)诮邮諅魅牍ぷ髁鞫x時(shí),都是接收 符合 BPMN2 規(guī)范的 XML 字符串,當(dāng)然好像也可以接收 json 對(duì)象,但是并不是都支持 json。
那么在了解了工作流的相關(guān)內(nèi)容之后,再進(jìn)入正題。
bpmn.js
首先,bpmn.js 目前 沒(méi)有 官方的中英文文檔,TypeScript 支持也依然不良好,討論區(qū)當(dāng)前只有官方提供的一個(gè)全英文社區(qū),如果你是一個(gè)才入前端不久又需要搞這個(gè)的話,建議你趕??!緊?。?!跑!?。。?!
開(kāi)個(gè)玩笑,這里先介紹一下 bpmn.js 的基本情況~
在 Camunda 平臺(tái)發(fā)展一段時(shí)間之后,由 camunda
團(tuán)隊(duì)內(nèi)部以 nikko 為首組織了一個(gè)小團(tuán)隊(duì) bpmn.io
,開(kāi)發(fā)了 bpmn.js
。
在進(jìn)入 bpmn.js 倉(cāng)庫(kù)就可以看到:bpmn-js - BPMN 2.0 for the web,也側(cè)面反應(yīng)了這個(gè)庫(kù)就是 純粹的為了在 web 頁(yè)面上處理 BPMN 2.0 規(guī)范工作流的。它的介紹也是:View and edit BPMN 2.0 diagrams in the browser,即在瀏覽器查看和編輯 BPMN2.0 圖。
bpmn.js 基于 SVG 實(shí)現(xiàn)網(wǎng)頁(yè)工作流的可視化編輯,通過(guò)內(nèi)部邏輯實(shí)現(xiàn) SVG 與 XML 之間的相互轉(zhuǎn)換,所以要使用這個(gè)庫(kù)起碼你的瀏覽器需要支持 SVG(死去的 IE 突然攻擊我~)
內(nèi)部,bpmn.js 依賴(lài) bpmn-moddle
與 moddle
兩個(gè)庫(kù)實(shí)現(xiàn) JS 對(duì)象與 xml
的轉(zhuǎn)換,再由 bpmn-moddle
與 diagram.js
實(shí)現(xiàn) JS 對(duì)象和 SVG 之間綁定與切換。
diagram.js
提供了最基礎(chǔ)的繪圖能力與 js 操控,并提供了基礎(chǔ)的工具能力模塊。
當(dāng)然,由于 bpmn.js 的團(tuán)隊(duì)本身就屬于 camunda,所以在對(duì) camunda 的支持上是十分優(yōu)秀的。
LogicFlow
由于筆者沒(méi)有深度使用過(guò) LogicFlow,所以如果有片面之處希望大家能及時(shí)指出。
對(duì)于 LogicFlow(后面簡(jiǎn)稱(chēng) LF),是由 滴滴 的團(tuán)隊(duì)開(kāi)源的一款圖形編輯工具,官方自己的定義是:一款流程圖編輯框架,提供了一系列流程圖交互、編輯所必需的功能和簡(jiǎn)單靈活的節(jié)點(diǎn)自定義、插件等拓展機(jī)制。
與 bpmn.js 一樣的是,LF 一樣也選擇了使用 SVG 來(lái)進(jìn)行頁(yè)面交互的。但是與 bpmn.js 不同的是,LF 則是 專(zhuān)注于提供可以快捷擴(kuò)展的圖形繪制能力,用來(lái)取代 bpmn.js、jsplumb 擴(kuò)展能力不足、自定義成本高的問(wèn)題。
當(dāng)然我個(gè)人覺(jué)得這里說(shuō) bpmn.js 擴(kuò)展能力不足也只是因?yàn)閷?duì)其核心實(shí)現(xiàn)不夠了解??
這里截取一段滴滴團(tuán)隊(duì)在掘金發(fā)布的文章 滴滴開(kāi)源 LogicFlow:專(zhuān)注流程可視化的前端框架 中關(guān)于 LF 定位的說(shuō)明:
- activiti 作為工作流引擎提供了前后端的解決方案,簡(jiǎn)單二次開(kāi)發(fā)就可以部署一套業(yè)務(wù)流程的管理平臺(tái)
- Bpmn.js:基于 BPMN2.0 規(guī)范,設(shè)計(jì)的流程圖編輯器
- G6:antv 旗下專(zhuān)注圖形可視化,各類(lèi)分析類(lèi)圖表。比如生態(tài)樹(shù)、腦圖、輻射圖、縮進(jìn)圖等等
- X6:圖編輯引擎,核心能力是節(jié)點(diǎn)、連線和畫(huà)布。不僅支持了流程圖,還有 Dag 圖、ER 圖
LogicFlow 的定位在上圖的 Bpmn.js 和 X6 之間,填補(bǔ)中間的空白。核心提供了流程圖的編輯器,并且通過(guò)拓展能力來(lái)支持 BPMN 等規(guī)范所需的流程節(jié)點(diǎn)和數(shù)據(jù)格式,以滿足當(dāng)前業(yè)務(wù)下的現(xiàn)狀。
可以看到 LF 在中間進(jìn)行了一些平衡,在 保留可以高度擴(kuò)展繪圖能力等情況下,通過(guò)擴(kuò)展來(lái)實(shí)現(xiàn)與 BPMN 的適配。
本身按照 LF 的這種設(shè)計(jì)和計(jì)劃來(lái)說(shuō),是能夠給開(kāi)發(fā)者帶來(lái)很大便利的,但是目前 LF 在工作流業(yè)務(wù)場(chǎng)景下仍然缺乏很大的完成度。
工作流業(yè)務(wù)下的選型
上面提到了,LF 這個(gè)庫(kù)的結(jié)構(gòu)設(shè)計(jì)是可以給開(kāi)發(fā)者帶來(lái)遍歷的,只是目前在工作流業(yè)務(wù)中依然存在很大不足;另外 bpmn.js 的擴(kuò)展性也是十分強(qiáng)大的,會(huì)讓人覺(jué)得難用的最大原因就是缺乏相關(guān)文檔——甚至是英文文檔(至于全量引入那個(gè)是有誤解的,已經(jīng)更換為 esm 的模式了)。
言外之意就是,指定工作流場(chǎng)景下依然建議使用 bpmn.js;當(dāng)然如果需求特別簡(jiǎn)單,也可以嘗試 LF。
這里比較一下他們的一些常用功能,關(guān)于 bpmn.js 的基礎(chǔ)原理可以查看我的這篇文章 Bpmn.js 進(jìn)階指南之原理分析與模塊改造
常見(jiàn)擴(kuò)展需求/功能
1. Render
LogicFlow
雖然 BPMN 2.0 規(guī)范中對(duì)每一種節(jié)點(diǎn)圖標(biāo)都進(jìn)行了詳細(xì)的標(biāo)注和說(shuō)明,但是那個(gè)黑白分明的突然肯定不符合現(xiàn)在老板們的審美,所以最常見(jiàn)的業(yè)務(wù)需求之一就是對(duì)顯示效果進(jìn)行修改,這里的顯示效果自然指的就是 render 部分。
在 LF 中,它默認(rèn)提供了以下 7 種基礎(chǔ)節(jié)點(diǎn):
-
RectNode
:矩形 -
CircleNode
:圓形 -
EllipseNode
:橢圓 -
PolygonNode
:多邊形 -
DiamondNode
:菱形 -
TextNode
:文本 -
HtmlNode
:HTML
每種基礎(chǔ)節(jié)點(diǎn)都有對(duì)應(yīng)的 Model
類(lèi)型,分別管理 View
顯示控制與 Model
屬性控制,開(kāi)發(fā)者分別可以繼承同一種節(jié)點(diǎn)的 View
和 Model
類(lèi)來(lái)實(shí)現(xiàn)自定義節(jié)點(diǎn),例如:
import { RectNode, RectNodeModel } from "@logicflow/core";
class UserTaskView extends RectNode {
getShape() {
const { model, graphModel } = this.props;
const { x, y, width, height, radius } = model;
const style = model.getNodeStyle();
return h("g", {}, [
h("rect", {
...style,
x: x - width / 2,
y: y - height / 2,
rx: radius,
ry: radius,
width,
height
}),
''
]);
}
}
class UserTaskModel extends RectNodeModel {
getNodeStyle() {
const style = super.getNodeStyle();
style.stroke = "blue";
style.strokeDasharray = "3 3";
return style;
}
}
export default {
type: "UserTask",
view: UserTaskView,
model: UserTaskModel,
};
bpmn.js
而在 bpmn.js 中,則是按照基礎(chǔ)流程節(jié)點(diǎn)類(lèi)型來(lái)繪制元素的,但是與 LF 不同的是,它沒(méi)有節(jié)點(diǎn)繼承關(guān)系。也就是說(shuō),一個(gè)類(lèi)型的節(jié)點(diǎn)有且只有一個(gè)對(duì)應(yīng)的渲染方法,只是 如果這個(gè)節(jié)點(diǎn)屬于某一種基礎(chǔ)節(jié)點(diǎn)的話,會(huì)在它的渲染方法內(nèi)部去調(diào)用對(duì)應(yīng)基礎(chǔ)節(jié)點(diǎn)的繪制方法。
默認(rèn)的 Render 方法在 bpmn-js/lib/draw/BpmnRednerer.js
中,可以通過(guò) this.handlers
找到。
其中 handlers
屬性是一個(gè)對(duì)象,包含了每個(gè) 頁(yè)面可見(jiàn)的 BPMN 節(jié)點(diǎn)對(duì)應(yīng)的 方法。
當(dāng)然,一些公共的 SVG 繪制方法還是抽離出來(lái)了。
例如我們以 StartEvent
開(kāi)始節(jié)點(diǎn)為例,它依賴(lài)與 Event
節(jié)點(diǎn),所以:
this.handlers = {
'bpmn:Event': function(parentGfx, element, attrs) {
if (!('fillOpacity' in attrs)) {
attrs.fillOpacity = DEFAULT_FILL_OPACITY;
}
return drawCircle(parentGfx, element.width, element.height, attrs);
},
'bpmn:StartEvent': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element, defaultFillColor),
stroke: getStrokeColor(element, defaultStrokeColor)
};
var semantic = getSemantic(element);
if (!semantic.isInterrupting) {
attrs = {
strokeDasharray: '6',
strokeLinecap: 'round',
fill: getFillColor(element, defaultFillColor),
stroke: getStrokeColor(element, defaultStrokeColor)
};
}
var circle = renderer('bpmn:Event')(parentGfx, element, attrs);
renderEventContent(element, parentGfx);
return circle;
}
// ...
}
可見(jiàn),handler
方法就是對(duì)每種節(jié)點(diǎn)進(jìn)行渲染并返回創(chuàng)建的 SVG 節(jié)點(diǎn),所以我們?cè)谧远x的時(shí)候只需要修改 handlers
對(duì)象即可,例如新增一個(gè) SqlTask
節(jié)點(diǎn):
this.handlers['miyue:SqlTask'] = function (parentGfx, element, attr) {
// 渲染外層邊框
const attrs = {
fill: getFillColor(element, defaultFillColor),
fillOpacity: defaultTaskOpacity,
stroke: getStrokeColor(element, defaultTaskColor)
};
renderer("bpmn:Activity")(parentGfx, element, attrs);
// 自定義節(jié)點(diǎn)
const customIcon = svgCreate("image");
svgAttr(customIcon, {
...(attr || {}),
width: element.width,
height: element.height,
href: mysqlIcon
});
svgAppend(parentGfx, customIcon);
return customIcon;
}
因?yàn)?SqlTask
節(jié)點(diǎn)是需要繼承 Task
類(lèi)型節(jié)點(diǎn)的,而 Task
最終又繼承 Activity
節(jié)點(diǎn),所以這里直接調(diào)用 Activity
即可。然后在 parentGfx
中插入我們的自定義內(nèi)容(parentGfx
即是這個(gè)節(jié)點(diǎn)對(duì)應(yīng)的 SVG 節(jié)點(diǎn))。
當(dāng)然,這是在我們需要顯示原來(lái)的
Activity
類(lèi)型節(jié)點(diǎn)的一些元素時(shí)才需要在內(nèi)部再次調(diào)用renderer('bpmn:Activity')
,如果是其他一些情況下(例如使用foreignObject
綁定div元素、直接插入圖片等)也可以直接在父元素下創(chuàng)建一個(gè)新的 svg 節(jié)點(diǎn)并返回。這里其實(shí) bpmn.js 并沒(méi)有對(duì)其做限制,因?yàn)樗?只負(fù)責(zé)在對(duì)應(yīng)的時(shí)候調(diào)用對(duì)應(yīng)的渲染方法。
2. 交互效果
可能是 web 編輯器的效果都類(lèi)似,在默認(rèn)情況下節(jié)點(diǎn)對(duì) hover 的響應(yīng)一般都是顯示一個(gè)邊框,在選中后增加一個(gè)高亮效果,這一點(diǎn)通常產(chǎn)品都不會(huì)對(duì)其進(jìn)行太大的改動(dòng),但是也不排除一些特殊情況:
- 查看流程圖時(shí)特殊顯示已流轉(zhuǎn)節(jié)點(diǎn)
- hover 時(shí)顯示辦理信息
- 顯示動(dòng)畫(huà)邊框或者連線動(dòng)畫(huà)
由于我對(duì) LF 不是很熟悉,所以這幾個(gè)場(chǎng)景我了解的也只有這樣的方案:
- 重寫(xiě) render 渲染部分
- 監(jiān)聽(tīng)事件顯示其他內(nèi)容
- 通過(guò)
graphModel
的 api 來(lái)動(dòng)態(tài)修改節(jié)點(diǎn)樣式
而 bpmn.js 也可以使用類(lèi)似的方式實(shí)現(xiàn),一樣可以重寫(xiě) render
渲染方法、監(jiān)聽(tīng)事件等、通過(guò) API 動(dòng)態(tài)修改等。
所以針對(duì)這個(gè)方面,兩者之間的差距并不大,解決方案也都大同小異。
3. BPMN 2.0 規(guī)范支持
上面也提了,bpmn.js 是 camunda 團(tuán)隊(duì)的成員進(jìn)行開(kāi)發(fā)的,本身的出發(fā)點(diǎn)就是為了支持 BPMN 2.0 規(guī)范的文件在瀏覽器上的編輯和顯示,所以對(duì) BPMN 2.0 的規(guī)范提供了一套完整的處理方法。
而 LF 則是一個(gè)具有繪圖能力的庫(kù),通過(guò)擴(kuò)展去支持的少部分 BPMN 2.0 規(guī)范,所以從這個(gè)層面來(lái)說(shuō) LF 肯定是不如 bpmn.js 的。
雖然在可視圖形的編輯和操作層面,LF 可以通過(guò)自定義來(lái)實(shí)現(xiàn)與 bpmn.js 一樣對(duì) BPMN 2.0 規(guī)范節(jié)點(diǎn)的完整支持。但是 LF 在深層的屬性處理與解析上,均是通過(guò)自定義屬性來(lái)處理,缺少約束;雖然這樣方便了開(kāi)發(fā)者最初開(kāi)發(fā),但是在生成 xml 階段則不太友好。
bpmn.js 是通過(guò) bpmn-moddle 與 moddle 兩個(gè)庫(kù)來(lái)實(shí)現(xiàn) js 對(duì)象與 xml 之間的轉(zhuǎn)換的,可以通過(guò) JSON Schema 來(lái)約束整個(gè)工作流中可以使用的節(jié)點(diǎn)與屬性,以及他們互相之間的關(guān)聯(lián)關(guān)系,對(duì)不符合的屬性或者條件,可以很快的找到問(wèn)題來(lái)源。
一點(diǎn)兒總結(jié)
總的來(lái)說(shuō),大家對(duì) bpmn.js 如此排斥的最大原因估計(jì)就是因?yàn)?沒(méi)有文檔,除了霖呆呆和我寫(xiě)過(guò)一些相關(guān)的文章,基本上剩下的都是大同小異的基礎(chǔ)使用文章,對(duì)于需要深度定制化開(kāi)發(fā)的小伙伴并沒(méi)有太多幫助。
也是因?yàn)檫@個(gè)原因, bpmn.js 在國(guó)外也飽受詬病,甚至有團(tuán)隊(duì)愿意花錢(qián)請(qǐng) bpmn-io 團(tuán)隊(duì)完善文檔都不了了之。而之所以不提供文檔,也是因?yàn)?bpmn-io 團(tuán)隊(duì)對(duì)他們編寫(xiě)的測(cè)試代碼有足夠的自信(看了一下確實(shí)很全面)。
bpmn.js 本身也提供了非常強(qiáng)大的自定義,通過(guò)依賴(lài)注入模式,可以很輕松的覆蓋或者擴(kuò)展原有內(nèi)容。
而 Logicflow 雖然在開(kāi)發(fā)體驗(yàn)上對(duì)沒(méi)有接觸過(guò)工作流的同學(xué)來(lái)說(shuō)會(huì)很友好,但是目前對(duì) BPMN 的支持依然還很欠缺,深度使用流程引擎依然需要進(jìn)行大量的自定義工作以及 XML 轉(zhuǎn)換工作,對(duì)于層級(jí)較深的一些定義還容易出現(xiàn)意想不到的bug。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-828633.html
所以在純工作流業(yè)務(wù)方向,還是推薦使用 bpmn.js 進(jìn)行前端頁(yè)面的開(kāi)發(fā)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-828633.html
到了這里,關(guān)于關(guān)于工作流開(kāi)發(fā)前端選型的一點(diǎn)個(gè)人見(jiàn)解(bpmn.js與LogicFlow)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!