1、研發提效還可以怎麼做
研發效能的提升一直是我們追求的主題,從最初的工具化到工程化,工程師們盡其所能去實現更快速地書寫程式碼,來應對不斷增長的業務需求,而後,小程式等各類平臺的崛起,工程師們又開始研究多端統一開發的解決方案,讓我們可以一次性寫出跨端執行的程式碼,進一步提升效率。但個性化的業務依然還在爆發式增長,那我們不禁要發出疑問,我們要如何繼續進行革新,來提升我們的研發效率。
我們思考「求變」,在智慧化思想愈來愈熱的當下,傳統的研發提效方式遭遇瓶頸,那我們是否能用智慧化的思想來解決呢?我們思索著,既然更快地寫程式碼這條路看似已經走得差不多了,那我們能否基於 AI 手段來實現程式碼生成,讓我們寫更少的程式碼,於是,我們探索的方向就轉向了「前端」+「智慧化」,希望藉助 AI 和機器學習的能力擴充前端能力圈,打通設計與研發的工作流程,實現規模化生產。
2、智慧程式碼——被選中的道路?
Deco 智慧程式碼專案是我們團隊在「前端智慧化」方向上的探索,我們嘗試從設計稿生成程式碼(Design To Code)這個切入點入手,對現有的設計到研發這一環節進行能力補全,進而提升產研效率。
在一個日常需求開發流程中,往往需要遵循固定的一套工作流程,產品提交需求 PRD,互動設計師根據 PRD 輸出互動稿,再由視覺設計師輸出產品視覺稿,接著再進入前端開發工作流。對於前端工程師來說,輸入源是視覺稿 + PRD,輸出結果是可上線的頁面程式碼。
Deco 期望解決的是上述流程中,對於前端工程師而已相對低價值,以及可用複用思想處理的工作:
- UI 視覺稿還原,即頁面重構,編寫 HTML + CSS;
- 可複用的業務邏輯繫結;
- 設計稿中已有元件的識別與替換
以「設計稿生成程式碼」為切入點,我們探索利用智慧化的解決方案來替代傳統的人工頁面重構(分析圖層樣式+切圖等),期望能從視覺稿原始資訊中提取結構化的資料描述,進而再與各類智慧演算法結合,輸出可維護的頁面程式碼。
Deco 經過 618 大促的初步驗證,隨後不斷升級打磨,在正在火熱進行的雙 11 個性化會場研發中已經廣泛投入使用,覆蓋 90% 左右的大促樓層模組,為業務研發帶來 48% 左右的效率提升。
3、如何實現一個設計稿生成程式碼方案
3.1、生成靜態程式碼
設計稿智慧生成程式碼的第一步是生成靜態化的程式碼,而這一步的核心是如何根據設計稿生成一份「結構化的資料描述」資訊,這份資料稱為 D2C Schema。
Deco 設計稿智慧生成靜態 程式碼主要做了兩件事情:
- 從視覺稿中提取「結構化的資料描述」;
- 將「結構化的資料描述」表達成程式碼;
本質上,Deco 智慧程式碼是通過設計工具外掛從視覺稿原始資訊中提取 D2C Schema,然後結合規則系統、計算機視覺、智慧佈局、深度學習等技術對 D2C Schema 進行處理,轉換為佈局合理且語義化的 D2C Schema JSON 資料,最後再借助 DSL 解析器轉換為多端程式碼。
3.1.1、處理設計稿
一份 Sketch 文稿是由若干圖層元資訊(分為 Document 和 Pages 等)和資原始檔(主要是圖片)組成的一個壓縮文件(檔案字尾為 ".sketch"),我們需要通過對圖層元資訊進行加工處理後得到一份供佈局演算法服務處理的資料。
通過開發 Sketch 外掛,使用 Sketch 提供的 API 來幫助我們去操作 Sketch 文稿,拿到圖層資訊後,對這些資料加工、篩選等處理。圖層資訊的處理主要是分為兩層:
- 設計稿加工層:將設計稿中的 Symbol 解耦成實際圖層,然後再對圖層進行各類處理,如過濾不可見圖層,合併必要圖層,處理蒙層等等;
- 圖層資訊處理層:提取圖層中有用資訊,將資訊進行轉換,同時去除掉無用圖層,打平圖層資訊等等。
下圖是對圖層資訊的處理流程:
除了對圖層資訊的基礎處理之外,我們建立了一系列的資料匯出的優化規則,用於增加布局以及語義的合理性。比如在一些大促設計稿上,複雜背景圖的設計可能是在一個圖層組下由若干個向量圖形組成(如下圖),如果原封不動地將這些圖層匯出,會給佈局帶來很多複雜度和不確定性。
在合圖的這一流程中,針對一個圖層組下所有圖層都是向量圖形的情況,我們會將它合成為一張圖片,這樣會大大減輕佈局的困難度。最終合圖效果如下圖:
當然,上面提到的這些優化規則並不能滿足所有的情況,畢竟設計師是自由的。為了提高佈局和語義的合理性,我們對入參的設計稿提供了一些規範協議供設計師以及開發者使用。
3.1.2、通過佈局演算法還原設計稿
設計稿外掛處理好的資料需要經過佈局演算法處理,來獲得視覺還原度良好、佈局結構合理的程式碼結構表達資料。
經過外掛匯出的元素資料,都是以左上角 (0, 0) 為座標原點座標的絕對定位為基礎的元素資訊,並且在一般情況下(無主動編組、無AI識別等等情況 )元素都是扁平化的,也就是元素間沒有從屬關係。
在前端開發過程中,絕對定位佈局無論是擴充套件性、可讀性都達不到開發要求,那麼如果不解決,就成為 一次性程式碼 。因此,需要佈局演算法來提高生成程式碼的擴充套件性、可讀性,供後續二次開發使用。
佈局演算法層的操作流程包含三大步驟:資料結構轉換、佈局推導、樣式計算。
資料結構轉換是將 Schema JSON 資料轉換為類似 DOM 樹的結構,可以進行節點插入、刪除、查詢操作。
在資料轉換處理之後需要進行佈局推導,在這一步進行行列分割推導,總體上包含:空間佈局演算法、投影佈局演算法、背景圖佈局演算法、特徵檢測佈局演算法、座標推導演算法、背景圖層及冗餘圖層檢測演算法等等。在佈局推導之後,Layout 結構已經有了明晰的層級關係及相鄰關係,我們就能獲得分割良好、成組合理,結構化的節點結構。
處理好佈局結構生成之後需要進行樣式計算,是對經過佈局推導層得到的結果進行一系列的計算,例如,基於層級關係,可以通過座標計算得出 Flexbox 主軸、側軸;基於相鄰關係,可以計算出相鄰之間的 margin 等等樣式。Deco 樣式大部分佈局採用 Flexbox,有些特殊情況需要使用絕對定位。
經過佈局演算法處理之後,我們就能獲得還原性良好,結構合理的 Schema JSON 資料。
3.1.3、生成語義化的程式碼
當設計稿資料經過佈局演算法處理後我們就能獲得結構較為良好的程式碼,但此時我們會發現由於節點元素缺乏相應的語義化類名,程式碼依然不具備很好地可讀性。為了最終能得到可以二次開發的程式碼,我們需要在佈局演算法之後加入語義化處理來讓程式碼擁有良好的語義性。
語義化首要解決的問題就是如何為元素節點加上具有語義化的類名。
為了實現這一目標,我們可以先回顧一下在我們開發的時候是如何給元素節點加上類名的,以如下的單個商品圖為例。
上圖是一個商品圖的示例,我們會通過圖片、價格、圖片下方文案等因素來判斷出這是一個商品,然後我們就可以給這一個區域賦予類名 goods
,而區域內的節點,比如圖片可以賦予類名 goods_pic
,圖片下方文案可以賦予類名 goods_tit
,價格可以賦予類名 price
,這就是我們為元素節點新增類名的一般邏輯。
可以看出,通常我們去確定一個區域,一個元件的語義時,我們需要依據區域內節點的語義組合才能進行判定,比如上面的商品元件,需要依靠內部的圖片、價格、文案等元素才能確定語義,從而確定類名。因此,語義化的處理方式,就是從容器元素的子節點出發,先確定子節點的語義,然後再推斷出容器元素的語義,一層層往上進行推斷,最終推斷出整棵節點樹完整的語義。
在語義化處理中,我們主要的處理物件就是經過佈局演算法處理後的 JSON Schema 資料,我們稱之為佈局樹,此時佈局樹已經具備了良好的結構,我們可以對它進行語義化推斷操作。推斷的流程就是從樹的葉子節點出發,一層層向上冒泡到枝節點,最後再冒泡到根節點。
目前我們進行推斷的依據主要是節點的位置、樣式、大小、兄弟節點等因素,同時會結合不同節點的型別,組合一些智慧化手段進行輔助推斷。例如,最小葉子節點一般可能為圖片、文字兩種型別,針對文字我們可以通過 NLP 的方式去分析文字的詞性、語義;針對部分圖片,我們可以使用圖片分類或識別的方式確定圖片分類或者提取圖片上的關鍵資訊進行圖片的語義判定。
為了確定每個節點的語義,我們需要組合一系列的規則對現有的事實(樣式、位置等資訊)進行推理,而同時,經過一些規則推理後又會得到新的事實,又需要經過其他規則推理之後才能得到最後確定的結果。所以,這是一個基於規則推理的推理系統,我們可以通過實現一個正向鏈的推理引擎,來幫助我們進行推理決策。
例如,推斷上述商品元件的過程,首先我們先找到具備價格因素的文字節點,命名為 price
,然後我們找到 price
附近,在樹中所處層級相近的圖片節點,並且該圖片節點符合商品圖大小的要求,這樣我們就能基本確定同時包含價格和符合商品圖特徵的容器為商品容器,再根據容器中元素個數,圖片附近是否有一段文字,以及對文字的 NER 分析,我們就能確定這段文字是否是商品名,從而確定其語義化類名。
在整個語義化處理中,上述的判定規則只是冰山一角,我們結合整個電商場景,分析了大量設計稿與線上案例後總結了大量的判定規則來幫助我們進行合理化語義命名,同時在語義化過程中採用 NLP 分析、圖片分類及識別等 AI 手段,來協助獲得更加精準的語義化名。
3.1.4、生成 DSL
經過前面幾步處理之後我們就能進行程式碼生成了,為了支援生成不同 DSL 的程式碼,我們將語義化處理後的資料統一抽象成類 Virtual DOM 的節點描述資料,然後再將節點描述資料處理成各類 DSL 程式碼,同時預留好介面,支援擴充新的 DSL。
3.2、讓程式碼擁有靈魂
在實現生成靜態程式碼之後,我們會發現有些時候設計稿中會出現已有的元件,最好的方式是我們能夠識別出設計稿中已有的元件,然後在生成程式碼的時候進行復用。
為了解決這一問題,我們將目光投降了深度學習,利用深度學習中的目標檢測與分類演算法,我們就能對設計稿中的已有元件進行識別、定位,並最終對映為元件庫中的元件,實現元件複用的目的。
元件識別對映整體處理流程是,針對輸入的設計稿圖片,先基於視覺演算法進行合理切割,在對切割後的圖片識別出圖片中包含的 UI 區塊,然後將 UI 區塊對映到 Schema JSON 中的節點資訊中去,而後再對區塊通過分類演算法識別出最有可能的元件型別,再將識別資訊寫入到 Schema JSON 的節點中,從而最終實現元件識別對映。
目前,我們已經完成了大促業務元件庫的訓練,可以精準地識別定位設計稿中的大促元件。但我們發現,如果我們想讓其他業務來接入元件識別對映方案時存在諸多困難,其他業務團隊不具備 AI 處理能力,如果都讓我們來進行元件訓練則會極大地增加我們團隊的工作量,為了解決這些問題,我們思考如何將 Deco 已經沉澱的 AI 能力進行共享,於是設計了 AI 開放平臺。
AI 開放平臺為開發者提供了零門檻接入的圖片分類、目標識別等 AI 能力,開發者可以在 AI 開放平臺上進行自定義資料集建立管理,選用預設演算法模型後就能直接進行模型訓練與預覽評估,這樣的話,如果有新元件庫需要訓練,就可以交給業務團隊通過 AI 開放平臺來自行操作。
3.3、在業務中進行落地
行文至此,我們已經具備了設計稿生成程式碼的整體方案,但離方案進行落地實踐還有一段距離,我們需要有一個平臺來承載我們的方案,當設計稿生成靜態程式碼出現誤差時,我們可以通過視覺化編輯器進行二次調整,同時在獲得靜態程式碼的同時,我們也可以再加上元件化、欄位繫結、生命週期等邏輯處理。
3.3.1、元件化
我們可以通過「元件標記」功能,將某一個節點標記為一個元件,標記為元件之後將可以進行設定元件內的狀態和資料、設定元件入參、設定元件生命週期等操作,並在最終生成程式碼的時候以元件形式生成。
3.3.2、資料定義
我們可以根據需要自行定義頁面全域性資料或者元件內部資料,同時也可以通過設定 React context 來進行共享資料定義。
3.3.3、非同步資料請求
針對最常見的非同步資料請求的場景,Deco 提供了一個視覺化表單,僅需通過簡單的配置,即可快速生成非同步資料請求的程式碼。
3.3.4、事件繫結
Deco 提供了包括點選事件在內的多種節點事件,以及元件的生命週期等事件的定義,使用者可以在事件中編輯邏輯程式碼。
3.3.5、屬性編輯及資料繫結
此外,Deco 已經提供元件對映的能力,在這基礎上,開放了元件的屬性編輯和資料繫結能力,實現頁面與動態資料的對接。
4、未來展望
如今看來,智慧程式碼是一條非常值得探索的道路,它是一粒種子,也許它此時剛剛發芽,但我們對它期許頗多,我們希望通過 Deco 來探索前端智慧化的道路,探索 AI 與前端結合的各種可能性,更重要的是,我們希望能夠通過 Deco 開啟產研的效率革命,在各種前端工程化、平臺、方法論趨於完善的當下,探索為業務降本增效的另一種方式。
同時,我們一直在智慧化上苦下功夫,也許在未來,我們可以直接實現設計即交付,那對業界來說無疑又是一場革新。
路漫漫其修遠兮,需要技術人上下而求索。