天天看點

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

一、從可視化搭建說起

頁面可視化搭建系統從16年開始如雨後春筍般湧現而出,從活動頁搭建到中背景搭建,有開源有僅公司内部使用的,都緻力于将前端從繁複的體力勞動中解脫出來,提高頁面生産效率。優酷内部也有一套營銷活動搭建系統,每年生産2K+活動頁;能夠滿足這麼多頁面的需求,除了沉澱了大量可複用的元件外,圍繞着搭建系統的前端研發每天都在不停地維護更新老的元件,同時生産新的元件。

痛點

頁面生産能力上去了,研發還是一直埋頭在元件開發需求中。這些需要都是從哪裡來的呢?其實上面也有提到,就是兩點:

  • 老的元件需要添加新的能力,可能是UI改動,可能是邏輯變更
  • 新的業務元件開發

需求是永遠也做不完的,為了提高開發效率,研發側不斷地沉澱通用的基礎庫,與服務端商定标準化的接口,以此來減少維護成本,但基于現有模式錦上添花的優化遠遠不夠。說白了,現有的可視化搭建效率和研發效率都已經達到瓶頸了,我們急需一種新的生産模式,給我們帶來生産效率的突破性提升。

解法

我們思考一下,頁面可視化搭建是如何解放生産效率的。它将完整的頁面進行拆解,拆分為可以複用的元件,研發負責元件生産,産品營運負責元件配置,形成了一種簡單的流水線作業的模式,這種模式的好處在于:

  • 業務元件複用,避免重複開發,研發隻需要專注于單個元件
  • 産品營運可以介入頁面生産過程,減少溝通損耗的同時分擔了部分先前研發承擔的工作

現在的瓶頸已從頁面開發效率轉變到元件開發效率上,我們做一個設想,讓非研發角色也能介入到元件生産過程中,進一步提高生産效率。在此之前,我們需要先基于現有模式進一步拆分元件結構:

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

元件可以拆解為 UI + 邏輯。UI 可以通過細粒度的文字、圖檔、slider等元件搭建出來;邏輯主要涉及到接口請求、資料處理、能力調用,我們将一些常用的api調用比如跳轉、常用的接口請求比如查詢登入态等進行封裝沉澱,加上語義化的描述,非研發人員可以将他們拖拽繪制成流程圖,完成業務邏輯的編排。兩者結合,就可以生産出完整的業務元件。而對于業務邏輯的組合編排,我們稱之為邏輯編排。

舉個栗子

我現在有個需求,需要實作一個簡單的抽獎活動。頁面分為兩塊,第一塊是獎池,五個獎品橫鋪開,第二塊是抽獎按鈕,點選按鈕,如果使用者沒登入,拉起登入面闆,如果使用者已登入,則進行抽獎并高亮展示對應的獎品。

我現在把UI撘出來了,就差邏輯來讓對應的坑位展示了,我們把欠缺的邏輯部分拆成兩段:

  • 進入頁面,也就是 componentDidMount 階段,查詢獎池
  • 點選抽獎按鈕時,查詢是否登入,未登入拉起登入面闆,已登入則調用抽獎接口
邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

除了開始和結束以外的這種邏輯片段都是我們已經沉澱下來的,可以直接拖拽進來,繪制流程圖。繪制完了後,我們選擇子產品的頁面主動觸發(設計給非研發用的,他們肯定不了解didMount),将它和查詢獎品關聯上;選擇抽獎按鈕的點選事件,關聯上抽獎邏輯,這樣子產品就能在不同的時機做正确的事情了。可能你還有點小疑惑,我查詢了獎品後,資料是如何映射到UI展示上的呢?要想知道如何映射,你得先知道邏輯是怎麼運作的。

下圖是我們在寒假戰役中的一個應用場景:

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

二、Logic is Core

扯一點遠的,不願意看可以直接跳到“邏輯如何運作”小節。前端的邏輯編排相比服務編排要更複雜一點,并不是說前端邏輯編排更難,而是因為:場景更加多樣化,C端子產品搭建和中背景搭建的編排差異就很大;人員更加多樣化,除了服務研發人員還要服務非研發人員;除此以外,還需要和UI進行結合,可能性就更多了。

這段時間比較火的 iMove ,初始被設計服務優酷活動搭建平台的邏輯編排,後面服務imgcook後,與優酷的邏輯編排在形态上已經截然不同,是以在前端比較難像服務編排一樣,平台化、中心化然後去服務多種多樣的業務。如果你真的很想去服務多樣化的場景及業務,提供一個輕量級的庫,讓它足夠靈活、可定制,這也是 iMove 正在走的路。

前面講了很多,都是在說,邏輯編排需要follow不同的平台做對應的定制,但是不管形态怎麼變,它的核心是不會變的,我們一定是在圍繞着 “邏輯” 去做包裝,将之打造為不同的産品形态。是以,邏輯才是核心!

邏輯如何運作

說了半天邏輯多重要,假使我現在抽離了一個函數出來,把它釋出了,我又用它拖了個流程圖,它怎麼才能執行呢?

邏輯最後想要運作,無論你是出碼(to code) 還是線上執行,你都需要一個“邏輯編排解釋器”,這個解釋器可以讀懂編排後的邏輯并且去執行邏輯,這個解釋器我們稱之為 Runtime。代碼是冰冷的,解釋器是沒有智商的,它并不能真的讀懂邏輯,隻是因為我們約定了一個規則,定好了編排後的邏輯可能有幾種情況,每種情況應該如何做,Runtime 隻是在依章辦事,而這個規則就是 DSL 。是以邏輯想要運作,它依賴于 DSL + Runtime ,要做一個邏輯編排平台,也一定是先定好 DSL,實作 runtime ,後面才是去做平台。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

每個沉澱下來的邏輯,我們将其定義為邏輯元件,加上語義化的元件名稱及詳細的描述,然後釋出到元件市場。使用者隻需要根據元件名稱來挑選需要的邏輯,通過連接配接線将他們連接配接起來,就可以組合成一個流程圖,這個流程圖也就是一段完整的邏輯。

可是流程圖導出來的 graph json 都是點和線的集合:

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

這種 json 如果直接用 runtime 執行有兩個問題:

  • 流程走向不直覺,每個元件執行完後需要浪費時間去查找下一個元件是哪個
  • 無效資訊太多導緻json體積太大進而影響加載性能,比如坐标資訊(x/y)、标簽資訊(label) ...

是以我們需要先去約定 DSL ,保證足夠直覺且隻包含必要資訊,是以我們設計了一個轉換器将 graph json 轉換為 DSL 。每個邏輯流程圖對應一個 DSL 産物,借助 runtime 的 interpret 方法,它可能會運作在 useEffect 中,也有可能運作在某個元素的點選事件中,或者是頁面的滾動事件中。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

邏輯元件生産與消費的分工

邏輯元件從被編排到被運作的過程,也是它被消費的過程。文章到這裡,相信大家可以感受到這個消費的過程,非研發同學确實是可以進入的,因為我們設計的足夠簡單。我們把整個頁面的生産當做一條流水線的話,以前是 子產品生産 --> 子產品市場 --> 頁面搭建,有了邏輯編排後,我們的流水線比之前劃分的更細了。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

這張圖是産品進入到中期的一個形态,前期的時候産品的角色更多是由研發來承擔的,然後逐漸過渡到産品,到了後期呢,産研聯合給營運同學做教育訓練,中間這部分會逐漸地過渡到營運同學那邊。

因為整條線上大家需要關注的事情越來越細了,頁面生産線出錯的幾率也會低一些。

三、邏輯編排

上面一直在從業務角度去聊,接下來就要深入到邏輯編排裡去了,講一講邏輯編排的設計思想。邏輯編排最主要是分為三塊,元件、編排器、runtime,我将它們稱作邏輯編排三闆斧。那問題就轉換成了:

  • 元件該怎麼做?
  • 編排器該怎麼做?
  • runtime該怎麼做?

這三塊各自拆解出基本要素,用基本要素來描述它們,互相之間建立起連接配接關系,這些構成了邏輯編排的規範協定。這樣不管什麼業務進來,隻要遵循這套規範,它們的底層就是一緻的,各業務也不會顯得散亂。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

DSL

在探讨我們的 DSL 之前,一定要先聊一聊DAG(有向無環圖),因為我們的 DSL 設計本質上就是 DAG 。

DAG

DAG 的 G 指 Graph(圖),圖是資料結構中最為複雜的一種,我們在了解 DAG 之前,回顧圖的幾個要點:

  • 頂點(vertices):圖中的一個點
  • 邊(edge): 連接配接兩個頂點的線段
  • 度數(degree): 從一個頂點出發有幾條邊,這個頂點的度數就是幾

圖就是由一些頂點和邊組成,邊就是頂點間的關聯關系。DAG 中的 D 是 Directed,代表是有方向的,就是說頂點之間的邊帶箭頭,常見的比如食物鍊,就是有向的;A 是 Acyclic,代表無環,從某個頂點出發,無論走那條路,都不會回到那個頂點。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

基于有向無環圖來約定我們的 DSL 正合适不過:

  • 我們需要有向邊來告訴我們邏輯的走向
  • 流程從開始節點出發一定要遇到結束節點才結束
  • 我們的邏輯元件可能有多個出口,就像頂點可能會有好幾個度,比如說判斷是否登入,就算是2度
  • 邏輯編排中我們沒法要求流程圖一定繪制成樹(一個頂點到另一個頂點,隻有一條路徑)那樣

基于DAG的DSL

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

每一個頂點都是邏輯元件的執行個體,繼承自邏輯元件,也可以修改自身屬性。邏輯元件分了兩大類 - 基礎元件和業務元件。基礎元件目前隻有開始和結束,其他所有需要研發開發的都是業務元件,這麼設計是為了降低編排使用門檻,你不需要有任何程式設計基礎。

  • type: 目前隻有三種類型,Start | End | Custom
  • func: Custom 類型專用,可以是函數體,可以是函數名,如果是函數名,需要提前在 runtime 中注冊
  • payload: 元件會開放配置項給營運配置,form表單資料會作為payload傳入給頂點對應的函數
  • next: 每個頂點都會有一或多個出度(出口),next指向目标頂點的uuid

跟 Flow-based programming 不一樣的是,我們移除了頂點之間的值的傳遞,營運不是研發,他們很難了解計算值如何流動以及如何操作它們。

Runtime

前面說到資料如何映射到UI時,不是故意賣關子,實在是要配合着 runtime 一起給您講解一下。邏輯與UI的結合這裡也隻是粗略提一下,要留到後面的文章細講。

Runtime與UI

React自身定位是用于建構UI的Javascript庫,它做的就是通過 data 驅動 UI 展示,react 的 data 通常都存儲在 state 中,我們剛才已經通過邏輯編排拿到了獎品資料,對此 runtime 唯一要做的就是在内部 data 與外部 react 中間架一座橋。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

将邏輯内部生成的資料全部存儲在内部 context 中,利用釋出訂閱模式,每當 context 有更新時,通知 UI 元件執行 setState,state 更新後,React 自動更新 UI。

runtime.subscribe('context', (val) => {  // handle data  this.setState({ data });})

輕量的Runtime

YOHO 的 runtime 很小,不到 10k,麻雀雖小,五髒俱全,接下裡我們看看這小麻雀是如何支撐起流程編排的最後一公裡 - “執行邏輯” ,又是如何和業務打交道的😊。

邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

邏輯元件本質上就是基于一段代碼,給它加了一些描述資訊,可是我的頁面中是不會内置這一段段代碼的,runtime 也不可能内置這些代碼,也就是說我們的執行上下文中是沒有元件對應的函數的;我們的業務是 No Code 形式,不出碼,是以 Runtime 内部實作了一個元件管理器,你可以通過他進行元件的注冊,在 DSL 執行過程中,它也會幫你進行元件檢查。

Runtime 對每一次編排執行個體(流程)的調用都是在沙箱中進行,執行個體的執行是互不幹擾的,而執行過程中每個元件輸出的結果我們也做了隔離;其實我們還給元件之間互相通信提供了方法,但是目前不建議使用,因為元件之間随意通信有很大的副作用,我們目前還沒有足夠的産品形态去限制它。

在 runtime 設計過程中,我們預見業務會不斷地有各種需求進來,可是我們不希望 runtime 過于業務定制化,導緻将來積重難返,是以設計了生命周期。在編排執行個體執行的各個階段,業務都可以進行幹預。

使用釋出訂閱模式,而不是觀察者模式的原因,也是我們希望 runtime 足夠靈活。舉例來說,上下文因為資料隔離,我們把它拆解為各個子上下文,你可以把每一個 childContext 當做一個 topic 來訂閱,其他 childContext 更新并不會附帶影響目前的。childContext 。

除了以上,runtime 隻做了一個事情 —— 邏輯走向的排程,就是有向無環圖中的 “有向”。正是基于以上的設計,我們的 runtime 足夠小,但是又足夠靈活,便于定制。

三闆斧在本文中就先講這第一斧😄

後話

在做邏輯編排平台的過程中,方案做過好幾版——從一開始兩周搞出 iMove 初版給老闆去展示,然後想要擁抱集團,和優秀的編排平台 Logic Force 想着共建前端編排,因為我們最終的産品形态差異過大,LF想要支援也需要投入很大的精力,同時我們的業務想要快速驗證,是以又重新自研了一個輕量的邏輯編排平台。期間有很多感悟,我們也都在邏輯編排的設計中添加進去了。

邏輯編排講到這裡還沒有結束,我們後面還會有系列文章,元件和編排器是如何設計的呀,和UI進行關聯,UI側又是怎麼設計的呀,我們在邏輯編排方面又做了哪些前端特色的東西呀?如果你也感到好奇,記得關注我們噢!

以下内容你可能感興趣

優酷播放體驗優化實戰(四)--“三高”音頻渲染引擎設計 ABF-新一代标準化中背景研發平台 ABF平台設計(二)-流水線的配置器
邏輯編排在優酷可視化搭建中的實踐(一) - 邏輯與Runtime

繼續閱讀