滴滴開源 LogicFlow:專一流程可視化的前端框架

前言

LogicFlow 脫胎於滴滴技術團隊在客服業務下的實踐,是由智能中臺—體驗平臺研發的一款流程可視化的前端框架,提供了一系列流程圖交互、編輯所必需的功能和靈活的節點自定義、插件等拓展能力,方便咱們快速在業務系統內知足類流程圖編輯器的需求。目前,LogicFlow 已經在公司內外不一樣用戶的流程配置需求中獲得了驗證。前端

背景

首先,智能中臺—體驗平臺技術團隊幾乎支持了滴滴全部業務板塊客服系統的訴求,面對多樣性、邏輯變動快的業務場景,傳統的面向場景編程成本高且週期長。所以咱們建設了線上配置化的運營系統,讓運營、產品同窗可以經過畫流程圖的方式變動線上的業務邏輯,好比用戶電話進線時的互動式語音應答、人工客服在處理用戶進線時的標準做業流程、用戶自助解決問題的 H5 頁面配置系統等千人千面的應用場景。vue

其次,各業務系統雖然都須要應用流程可視化技術,但需求各不相同。有的對流程圖的要求比較簡單,圖的數據格式也簡單,而有的須要按照 BPMN 的規範來繪製流程圖,對於定製化的要求較高。咱們調研了市面上相關的框架 (BPMN.js、X六、Jsplumb、G6-editor),均存在不知足的場景,技術棧統一的成本很高。具體表如今:node

  1. BMPN.js、Jsplumb 的拓展能力不足,自定義節點支持成本很高;只能全量引入,各系統沒法按需引入
  2. 與後端配套的流程引擎適配,成本較高。均不支持數據轉換、不支持流程的校驗等業務定製需求。
  3. 文檔、示例不健全。X6 和 BPMN 的文檔不健全,示例少(2020 初調研結論)

所以,咱們在 2020 上半年開啓了 LogicFlow 的項目,支持各系統的流程可視化需求。react

LogicFlow 的能力和特性

LogicFlow 當前已具有了哪些能力呢,我會分兩部分來介紹。git

快速搭建流程圖編輯器

提供了一個流程圖編輯所必需的各項能力,這也是 LogicFlow 的基礎能力:程序員

  • 圖的繪製能力。基於 SVG 來繪製形狀各異的節點和線,並提供了基礎的節點(矩形、圓形、多邊形等)和線(直線、折線、曲線)
  • 各種交互能力,讓圖動起來。根據節點、線、圖的各種鼠標事件(hover、點擊、拖拽等)作出反應。好比節點拖拽、拖拽建立連線、線的調整、雙擊節點編輯文本等
  • 提高編輯效率的能力。提供網格、對齊線,上一步、下一步,鍵盤快捷鍵,圖放大縮小等配套能力,幫助用戶提高編輯效率
  • 提供了豐富的 API ,宿主研發經過 API 傳參調用和監聽事件的方式,與 LogicFlow 完成交互

經過以上能力,前端研發能夠低成本、快速的搭建起流程可視化的應用,提供流暢的產品交互。下面是經過 LogicFlow 內置的節點和配套能力,作的流程圖示例:github

example1

基於業務場景拓展

當基礎能力沒法知足業務需求的時候,便須要基於業務場景拓展。這也是 LogicFlow 能支持客服側多個系統的關鍵所在。算法

  • 設置圖上全部元素的樣式,好比各類節點、線、錨點、箭頭、對齊線的大小顏色等,知足對前端樣式調整的需求
  • API 拓展。支持在 LogicFlow 上註冊自定義的方法,好比經過 API 拓展提供圖片下載的方法
  • 自定義節點、線。內置的矩形、圓形等圖形類節點每每沒法知足實際的業務需求,須要定義具備業務意義的節點。LogicFlow 提供了 的方式讓用戶定製具備自定義圖形、業務數據的節點,好比流程審批場景中的 「審批」 節點
  • 拓展組件。LogicFlow 在 SVG 圖層上提供了 HTML 層和一系列座標轉換邏輯,並支持在 HTML 層註冊組件。宿主研發能夠經過 LogicFlow 的 API,基於任何 View 框架開發組件,好比節點的右鍵菜單、控制面板等
  • 數據轉換 adapter。LogicFlow 默認導出的圖數據不必定適合全部業務,此時能夠經過 adapter API,在圖數據從 LogicFlow 輸入、輸出的時候作自定義轉換,好比轉換成 BPMN 規範的圖數據
  • 內置部分拓展能力。基於上述拓展能力,咱們還單獨提供了 lf-extension 的包,用來存放客服業務下沉澱出的具備通用性的節點、組件等,好比面向 BPMN 規範的節點和數據 adapter,默認菜單。注意 lf-extension 能夠單獨安裝,並支持按需引入

基於上述拓展的能力,前端研發可以根據實際業務場景的需求,靈活的開發出所需的節點、組件等。下面有兩個基於 LogicFlow 拓展能力作出的流程圖:編程

BPMN:小程序

圖片:bpmn

審批流程:

圖片: 審批流

定位對比

dingwei1

上圖是經過橫縱兩個維度來對比目前你們耳熟能詳的幾個開源框架,以瞭解 LogicFlow 的定位。橫軸是該框架在圖可視化能力的豐富程度,縱軸越靠上則表明這個框架在業務流程應用上的成熟度越高,初次部署的開發成本越低。 讓咱們分別來介紹一下這幾個框架:

  • activiti 做爲工做流引擎提供了先後端的解決方案,簡單二次開發就能夠部署一套業務流程的管理平臺
  • Bpmn.js:基於 BPMN2.0 規範,設計的流程圖編輯器
  • G6:antv 旗下專一圖形可視化,各種分析類圖表。好比生態樹、腦圖、輻射圖、縮進圖等等
  • X6:圖編輯引擎,核心能力是節點、連線和畫布。不只支持了流程圖,還有 Dag 圖、ER 圖

LogicFlow 的定位在上圖的 Bpmn.js 和 X6 之間,填補中間的空白。核心提供了流程圖的編輯器,而且經過拓展能力來支持 BPMN 等規範所需的流程節點和數據格式,以知足當前業務下的現狀。

實現原理和架構

總體架構圖

圖片: lfjk

核心包 @logicflow/core 提供了流程圖編輯器基礎的能力,右邊的 @logicflow/extension 是基於 @logicflow/core 的拓展性開發的插件。

流程圖編輯器的設計方案

主要介紹一下實現流程圖編輯器重要的選型和方案設計。

圖渲染方案

前端繪製圖形無非就是 HTML + CSS、Canvas、Svg 三種方式,咱們綜合作了一下對比,列出了相應的優劣勢:

bijiao1

在流程圖的場景下,不須要渲染大量的節點(最多幾千個元素),對於動畫的訴求也不高。Svg 基於 DOM 的特性會更適合咱們,一個是學習成本和開發成本更低,另外一個是基於 DOM 能夠作的拓展也更多。不過 Svg 標籤內部並不支持插入其餘好比 div 這種標籤,因此在實現某些功能的時候,都須要結合其餘 HTML 標籤。

因此最終咱們選擇使用 HTML + Svg 來完成圖的渲染,Svg 負責圖形、線的部分,HTML 來實現文本、菜單、背景等圖層

模塊抽象

基於上述方案,下一步咱們要作的是對實現一張流程圖作分類和抽象。

圖片: mkcx

經過上圖:

  • 首先咱們構建了多個圖層來承擔不一樣的職責,以方便實現功能和能力拓展。最上層的是 Svg 圖層,全部圖形(節點、線、對齊線、outLine 等)均在 Svg 上渲染,也負責監聽圖上的各類事件。Svg 下層的分別是組件層,負責拓展 UI 組件;Grid 層,負責渲染網格;背景層,添加自定義的背景。
  • Shape 的職責主要是基於 Svg 對圖形渲染的封裝,提供默認樣式、把用戶傳入的屬性作轉換等,主要包含 Rect、Circle、Ellipse、Polygon、Path、PolyLine、Text 等,方便 LogicFlow 內部複用,好比圓形節點和錨點都須要 Circle。
  • 基於 Shape,還實現了不少小元素,好比節點和線須要的錨點,好比線上的箭頭等等。
  • 而 BaseNode、BaseEdge 則是節點和線通用能力的封裝,聚合 shape、錨點、文本,還封裝了對事件和樣式的處理等。經過繼承 BaseNode,傳入 shape 咱們能夠獲得 RectNode、CircleNode 等可渲染的節點。

由於流程圖是富交互或者說是重編輯的,有了這幾個基礎的模塊,接下來要作的就是富交互的方案設計,即用戶在圖上作的任何操做都要給出響應。好比我觸發一個節點的拖拽,那關聯的線可能須要跟着動,還能識別出在某個水平線上有沒有其餘節點(對齊線)。

MVVM + Virtual DOM

首先咱們考慮到整個圖編輯器具有不少狀態存儲,而且要實現編輯圖上各模塊的響應就必需要有狀態的通訊能力。第二若是要實現相似 redo/undo 這類功能,那整個圖就必定須要根據數據得出渲染,即 fn(state) => View ,比較好的方式就是經過 Model 來驅動 View。

最終咱們選擇基於 MVVM,這個普遍被應用於當前前端工程中的設計模式來構建 LogicFlow 的圖編輯器,定義圖的 View 和 Model 層,使工程代碼具有必定的解耦。與此同時,引入 Mobx 來實現咱們的狀態管理、數據響應的能力,一張圖基於一份 Model 作狀態的通訊。此外,考慮 Mobx 的另外一個緣由是:只要我想,那就能夠作到最細顆粒度的數據綁定(觀測),能夠減小不必的渲染。

如下是 LogicFlow 圖編輯器的 MVVM 示意圖: 圖片: mvvm

經過上圖能夠看到,View 層(圖、節點等)經過數據綁定,會在 Model 發生變化以後作出響應/更新。前面咱們提到了關於圖的渲染咱們是基於 Svg + HTML 實現的,那要作 View 層的更新無非就是命令式和聲明式兩個選擇:

  • 命令式。好比 jQuery 的 api,$('.rectNode').attrs({x: 1, y: 2}),像這種方式操做 DOM 代碼其實比較繁瑣,在重交互的場景下寫的代碼會比較冗餘。雖然咱們最終找到了有一個庫可以很方便的支持經過命令式的方式來繪圖 —— antv/g
  • 聲明式。好比 React/Vue 這類 View 框架,其中一個比較核心的能力就是作到了 state => UI ,經過聲明式的方式來構建 DOM,只要狀態發生變化,那 UI 就更新

除了考慮到命令式在操做 DOM 的場景下寫代碼會比較繁瑣以外,還有一個緣由就是操做 DOM 的成本問題,在基於 State 更新 UI 的設計下,咱們天然而然想到了引入 Virtual DOM 來解決某些場景下的更新效率,這也能夠必定程度上彌補「基於 Svg 渲染圖形」可能形成的渲染性能問題。

總之,選擇 MVVM 的設計模式並引入 Virtual DOM,最根本的兩個緣由即是提高咱們圖編輯器場景下的開發效率,以及在 HTML + Svg 的圖渲染方案下,能夠追求更好的性能表現

咱們與 X6 作了一次渲染時的性能比較,在相同的運行環境下,分別測出 LogicFlow 和 X6 在不一樣量級的節點/線下,渲染出流程圖的時間,理論上渲染時間越短,性能表現越好。

對比1

對比2

經過上述表格,咱們測算出 LogicFlow 在初始渲染速度上是優於 X6 的,而且這尚未開啓LogicFlow 的按需加載功能,也驗證了咱們的技術選型。你也能夠在示例頁進行測試: yhlchao.github.io/LF-VS-Other…

事件系統

介紹了在 「狀態」 和 「響應」 咱們作的設計,那要收集到用戶的各種 「操做」 並及時上報和冒泡,就須要一套事件系統。最主要的就是複用和統一上報。

圖片: Event

複用即怎麼保證全部節點和線都能具有默認的事件回調,以及針對復瑣事件(拖拽)的處理邏輯如何共用。

  • Behavior。針對復瑣事件的處理,咱們作了 function 和 class 形式的封裝,好比 Drag 是經過 mousemove、down、up 來模擬 h5 的 dragEnter、dragOver、dragEnd 和 drop 事件,DnD 則是經過抽象 dragsource 和 droptarget 兩個實體來實現 drag 和 drop 的交互,好比拖拽建立節點
  • 在前文模塊抽象章節提到了內部有 BaseNode 和 BaseEdge 這樣的抽象,內置節點和自定義的節點都經過繼承基類來得到通用的能力,因此 LogicFlow 內部默認的事件回調實際是經過繼承來複用的
  • EventCenter。經過事件總線作統一上報,把內部捕獲到的全部用戶行爲事件,按照必定的規範和格式emit(ev, args)都上報到 EventCenter,最終冒泡到 LogicFlow 類,由 LogicFlow 類統一跟宿主交互。此外,圖編輯器內任何地方也均可以經過 EventCenter 作事件的觸發和監聽

工具中心

工具中心的定位是解決某類特定問題的 utils,好比上面提到的 Behavior(復瑣事件的封裝) 和 EventCenter。此外,在圖編輯的過程當中,若是要實現比較好的交互效果,實際有不少複雜的計算邏輯要處理。

  • 座標系。瀏覽器的 clientX、clientY 座標系,以及 Svg 圖自己的座標系,當出現圖的縮放和平移的時候,兩個座標系顯然是不一樣的,那如何作座標系的轉換。

  • Algorithm。是專門經過幾何、算法來處理可視化的一些問題。好比:當一個節點在同一方向有多條折線連出的時候,如何作路徑的合併以展現起來更美觀,以下

    圖片: shili1

    如何計算出一根線到一個圖形的切點,以達到線能夠鏈接圖形非錨點的位置,以下圖

    圖片:shili2

  • History,主要提供 redo 和 undo 的能力。經過兩個棧來存儲 undos 和 redos,並限制最大長度,得益於 MVVM 的設計模式,能方便的作數據變化的觀測和 Model 驅動 View。

可擴展性

介紹完流程圖編輯器的設計方案,如今來介紹 LogicFlow 的另外一個重要特性,關於拓展性方面的設計。在程序世界中,小到一個 function,一個服務,再到一個開發框架 react,小程序開發框架,大到一個 Chrome 類的應用平臺,都具有本身的可擴展性,這也是軟件發展過程當中要考慮的一種設計選擇。對於 LogicFlow,是解決某個領域問題的開發框架,首先 API 要具有可擴展性;此外 LogicFlow 還提供了視圖層,在 View 部分應該可以讓用戶作二次開發。這兩個擴展的方向肯定以後,最主要的仍是結合業務需求,要能知足當前和將來一段時間內預見的業務場景,但也不能過分設計。

API 上的設計

首先,LogicFlow 在面向用戶使用這一層,徹底是基於面向對象的設計模式封裝的,最大的好處是幾乎每一個程序員都熟悉它的使用,使用成本低。經過下面初始化方式即可以瞭解。

const lf = new LogicFlow({ // 實例化 lf 對象
  container: document.querySelector('#graph'), // 獲取渲染容器
  width: 700,
  height: 600,
  tool: {
    menu: true,
    control: true,
  },
  background: {
    color: '#F0F0F0'
  },
  grid: {
    type: 'dot',
    size: 20,
  },
});
lf.render({ nodes: [], edges: []}); // 在界面上渲染視圖
複製代碼

經過 class LogicFlow,用戶實例化一次便獲得一個流程圖的實例,狀態也是私有的,各類使用方法經過 lf 的實例調用便可。 關於 API 拓展的設計總結來看:

  1. 面向對象的設計模式, LogicFlow 內部作好封裝,用戶能夠作繼承、重寫接口/方法
  2. 方法的設計。首先是要有固定類型的輸入和輸出。此外,LogicFlow 也提供了相似於 extends 的方法,經過 LogicFlow.use(fn) 在原型上拓展方法
  3. 經過觀察者的模式作通訊,即提供 on 方法供宿主訂閱各種內部事件
  4. 圖的數據可定製。不管是一個節點、線有哪些自定義的業務屬性,仍是流程圖要導出什麼樣的數據,都應該可以定製。

插件化

View 層的拓展性,除了用戶可以定製展示方式以外,最重要的是插件化,由於在流程可視化這條路上,不一樣的業務場景下須要的能力不盡相同,LogicFlow 很難作到支持全部的場景,因此提供好的插拔能力,讓用戶二次開發是比較好的選擇。目前,在 UI 界面上,咱們開放了兩個能力:

  1. 節點和線支持二次開發,即自定義節點、線
  2. 可開發 UI 組件註冊到 LogicFlow 的組件畫布內

基於插件化的思路,咱們已經支持了不一樣的業務系統,並在這個過程當中把一些稍微通用的能力沉澱出來,並封裝到 lf-extension 包,好比用來支持 BPMN 規範的節點。目前 extension 內的拓展主要分了四類:UI 組件、自定義節點、API、adapter。

將來規劃

  1. API 的易用性和豐富程度。具體的功能 scope 除了咱們當前的迭代計劃(詳見 github 倉庫的 project),還會根據用戶的需求排出優先級後加入進來,也但願你們多多提意見和需求。這個方向的基調是保持 LogicFlow 流程可視化的定位,把 core 的 API 豐富,extension 的能力加強
  2. 更完善的文檔和示例。主要是文檔易讀、完善,可以有完整的示例和代碼,供開發者 copy paste 代碼,目前示例只有 react 版,2021.4 以前會增長 vue 版的示例
  3. 不只是流程可視化庫,指望提供整套解決方案。LogicFlow 只解決了前端流程圖編輯的技術問題,但關於圖數據的定義,流程最終如何被執行,還須要一個配套的流程引擎。目前,關於「流程引擎」咱們團隊也有相應的解決方案 —— turbo(Java 版已開源:github.com/didi/turbo) 咱們會把 LogicFlow 和 turbo 作成端到端的解決方案,並提供完整的應用示例。此外,Nodejs 版的引擎也在規劃中,你們拭目以待。

最後

相信你對 LogicFlow 已經有一個大概的認識了,若是在你負責的業務中也有流程可視化的訴求,而且有較高的拓展性需求,那 LogicFlow 會是一個好的選擇。對於 LogicFlow 技術自己的實現細節、對於類似業務的探討也都歡迎你們來交流。咱們後續會有更多的文章介紹 LogicFlow 在技術設計細節以及咱們對於可視化、業務流程、邏輯編排等領域的一些思考,盡情期待。

相關文章
相關標籤/搜索