天天看點

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

作者簡介

鄭勇,攜程進階技術經理,目前主要負責CRNWEB架構的開發工作,以及在攜程内部的使用推廣和性能優化。

前言

React-Native自從2015年推出,就一直火到了現在,一度在技術圈言必RN,激發一波廣泛的思潮。攜程基礎業務研發團隊迅速跟進,在React-Native基礎之上,開發出了CRN這一适合攜程業務高速發展的、抹平了iOS和Android端元件開發差異的、做了大量性能提升的架構。然而無論是CRN還是React-Native本身都無法解決移動闆塊中的一大版圖——WEB平台。

而現實是:存在大量的業務需求需要三端的支援,單獨再開發一套H5成本高昂,後期的維護成本也很高,需求同步難,使用者體驗不一緻等問題都會非常明顯,而攜程基礎業務前端架構團隊一直都在緻力于解決iOS和Android之後,将BU業務代碼無縫接入WEB平台的技術方案,于是CRN-WEB(簡稱CW)應運而生。

一、CRNWEB是什麼?

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

CRN-WEB的使命就是在CRN和React-Native的基礎之上,建構一個三端打通的平台,能夠實作BU的一套業務邏輯代碼,能夠根據平台情況運作在三端之上,并帶來使用者體驗上的一緻性(和React-Native保持一緻)和優越性(使用Virtual DOM,PWA等技術提升性能)。

1、設計共性

對于CRN-WEB這樣一個架構,我們在設計之初就可以提取一些軟體設計方面共性的問題:

1)易用性,CW架構必須簡單易用,大幅度降低開發成本、運維成本和學習成本,将是這個架構的核心價值,如何做到呢?

2)一緻性,和現有技術架構的內建問題,即如何将CRN-WEB與CRN和React-Native進行友好的內建,各自發揮各自的功能,如何保證各平台間的一緻性?

3)穩定性,React-Native版本疊代迅速,版本間差異較大,既然三端打通,共用BU源碼,那麼BU的React-Native項目或者CRN項目在接入CW架構後,必須能夠穩定運作在WEB平台上,如何保證項目穩定運作?

4)相容性,WEB平台是非多(浏覽器廠商多,版本多,私有規範多,差異多...),相容性問題一直是WEB項目開發頭疼的事情,如何處理好相容性問題?無疑是非常棘手的。

5)擴充性,包括React-Native本身都還在不斷的變動,增加新功能,再加上公司級别的功能性需求,業務級别的功能需求,将令如何保持架構擴充性變得非常麻煩。

2、我們的設計思想

That‘s a big business,的确,這些問題很難處理,但是經過深入的思考,我們提出了這樣的設計思想。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

React-Native為解決iOS和Android兩端相容提供了解決方案,它是如何做到的呢?當然RN團隊經過了大量的工作和思考,最終他們提供了一套規範,即React-Native,與其說它是一個架構不如說它是一套規範,對,我就是這麼認為的。

如果CRNWEB的設計也基于React-Native的規範,把React-Native抽象成一個邏輯層,為不同的平台提供相同的Component和API輸出和相同的APP主要運作流程,然後在規範之下各個平台各自實作,即iOS Implement,Android Implement,WEB Implement,那麼從設計上來看是比較完美的。

對于業務方而言如Flight項目,Hotel項目等等,無需關心底層的技術實作,使用React-Native這一套開發技術體系基本上就足矣。

3、設計優勢

這樣設計同時還可以解決好幾個問題:

比如易用性,我們采用了React-Native的規範,那麼我們就可以使用開發人員熟悉的技術,熟悉的規範,熟悉的知識,熟悉的流程,無需額外學習太多其它規範和技術棧。

否則BU的學習成本,接入成本太高,起不到降低開發成本的作用,當然為了解決易用性,還有很多其它方面的工作,比如提供一整套的開發流程,開發工具,釋出工具,技術支援等等。

再比如一緻性問題,和React-Native,CRN使用相同的規範,這樣的設計保持了天然的一緻性。

二、CRNWEB是如何工作的呢?

我們依然從程式設計的傳統,Hello wolrd開始。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

熟悉React-Native的同學可能一眼就能夠看出來,這完全就是React-Native的原代碼,你說的對,它不僅是一份RN的源代碼,也是一份CRN-WEB的源代碼。它雖然是一個最簡單的Hello World,但是它幾乎包含了React-Native的Component和API,以及主要的運作流程。

1、主題結構分成三個部分:

1)頭部的依賴部分,使用ES6的文法import,導入依賴的程式包React和React-Native;

2)中間部分實作子產品主要邏輯;

3)尾部使用ES6文法export導出子產品輸出;

在CRN-WEB中也是這樣,毫無差異。

2、那麼CRNWEB是如何讓和React-Native相同的源代碼運作在Web平台的呢?

要實作這種能力,那麼它必須滿足兩個最重要的必要條件。第一點,我們要實作在Web平台上面,跟React-Native上面具有相同功能的Component和API,比如這裡的View和Text,這個就是我們後面要講到的元件系統。

第二點,我們要有一種機制使得我們的React-Native原代碼能夠在Web上面運作起來,調用我們WEB平台上的Component和API,使得我們對代碼擁有足夠的控制能力。這個就是我們後面要講到的打包系統。

三、運作分析

HelloWorld代碼編寫完成,配置好環境,執行CRNWEB指令,檢視編譯後運作效果和運作結果。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

1、入口元件

CRNWEB仍然使用了AppRegistry作為程式入口注冊元件,當然這裡的AppRegistry已經被換成了AppRegistry.web這個WEB版本的Implementation,AppRegistry實作了registerComponent作為程式入口,承擔App初始化工作,例如:

1)運作環境初始化,例如識别是h5還是hybrid;

2)注入預設的全局性樣式,例如抹平浏覽器差異的樣式;

3)全局性請求參數的解構和傳遞;

4)初始化全局性元件的容器等等;

2、同步元件的異步轉換

HelloWorld元件就是一個标準的React-Native元件,在CRNWEB中為了提高性能,将HelloWorld元件轉化為異步元件HelloWorld(__CRNWEBFUNCTION__),進而實作頁面級别的按需加載,僅在需要的頁面運作時進行加載。

這在WEB環境下是非常重要的一項優化,這是專門針對WEB環境下脆弱的網絡環境而作出的改進,特别是在頁面衆多,元件數量大,元件體量大的較大型WEB項目中,性能提升非常顯著,這在BU的實踐中得到了的認可。

3、具體的業務邏輯頁面的編譯轉化

而原來的HelloWorld業務邏輯被打包到了子產品号為97的package中,并處理好了它的依賴,如下:

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

我們可以看到原來的ES6文法書寫的代碼被編譯成了ES5文法的代碼,因為ES5在WEB環境下有着更廣泛和友好的支援,相容性更好。而HelloWold中引入的View,Text,StyleSheet等等元件,也全部變成了WEB版本的具體實作,這裡使用了一招瞞天過海。

4、元件系統

而View,Text等等衆多的React-Native原生元件對應的WEB版本的具體實作,就構成了CRNWEB的元件系統,篇幅有限不做展開。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

5、樣式處理

而HelloWorld裡引入的StyleSheet就是樣式處理系統中的入口檔案。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

CRNWEB的樣式處理系統我們主要提供四種方式:

首先是APPRegistry,我們需要注入一些預設的全局樣式,這個前面已經有所提到。後面三種其實都是對于元件樣式的處理。

第二種是對元件的預設樣式,可能有一些元件,曆史的元件,我們也給它提供了這種能力。

第三種是一種預處理,元件樣式的一個預處理,基本上都要用到StyleSheet.Create,這個和React-Native保持一緻。

第四種我們對樣式的一個實時處理系統。

樣式處理系統的任務就是處理樣式的問題,包括但不限于:

1)平台間樣式的差異性,比如Border,在React-Native下,它是分散的每一個屬性值進行一個獨立的編寫,而在Web上面它的Border是一個混合制,是以這就是平台之間的差異,CRNWEB架構就會開一個任務去對它進行修複。

2)浏覽器間的差異,比如有的浏覽器支援FlexBox,有的不支援,而且即使是支援FlexBox,支援的程度,版本也不一樣,這些都是需要具體處理的修複任務。

3)一些共性上的問題,如機關處理,顔色處理等等。

4)一些差異性樣式問題,如字首處理,視口問題。

5)Web不支援的樣式,如BoxShadow的實作等。

6、事件處理

CRNWEB中還有非常重要的一大塊邏輯就是事件處理,我們專門提供了一個事件處理系統來進行處理。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

我們使用了PanResponder,它提供一個對觸摸響應系統的Responder的可預測的包裝,和React-Native保持一緻的事件處理流程,是以在事件的處理流程和相容性方面和React-Native保持了高度一緻性。

四、打包系統概述

最後CRNWEB的一大重頭戲就是打包系統。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

1、打包任務

CRNWEB打包系統的任務非常多,從流程上看,大概分為以下幾個階段:

1)Prepare階段,需要對它進行入口檢查,版本檢查,環境檢查以及第三方的依賴檢查等等,各種的預先準備條件都在在這個階段進行處理。

2)進入到Webpack的打包建構流程,我們編寫了很多Webpack的插件,對它打包進行各種處理和優化。

3)建構過程當中會去調到Babel,利用Babel對原代碼進行編譯,對文法進行處理,對于代碼的同步、異步轉換這樣一些比較核心的内容,都是通過編寫的Babel插件對原代碼進行處理實作的。

4)進入到Create階段,因為有的Bu需要生成JAVA工程,有的需要.Net的工程,還有的隻需要一個Static靜态工程,在這個階段需要對它進行一個工程的一個建立。

5)對這個工程進行啟動,我們提供了開發版和生産版,他們的側重有所不同。像開發版的話,它的主要訴求是打包編譯的速度要快,這樣才可以提高效率。而對于生産版它最核心的需求就是,要使你的size最小化,使你的運作效果最好。我們使用了很多優化手段對它進行了處理。

6)最後涉及到的一塊,需要對它的size,它的依賴進行各種各樣的優化。我們實踐下來發現,BU代碼量是非常非常多,業務也是非常非常複雜。怎麼辦呢?我們對業務工程進行size的分析,依賴分析等等各種更深入到代碼層面的分析和處理,進而尋找到最佳實踐的解決方案。

2、一些關鍵優化點

随着業務蓬勃發展,頁面越來越多,元件越來越大,無論對于Native還是對于Web來說,這都是無法回避的挑戰,精簡打包size成為重要工作,對于size這一塊我們做了很多優化處理,包括但不限于:

1)對于React,進行了優化和增減,以及一些切入式的處理,隻保留需要的部分。

2)使用了tree shaking技術,排除掉了很多死代碼。

3)對元件進行進階别抽象,增加重用度。

4)減少元件層級,簡單而有效的方案,既減少size又提升性能。

5)運用各種cache技術,提升使用者體驗。

幹貨 | 揭秘攜程三端通用架構中的CRNWEB

另外我們使用了一些工具,能很好的将項目中的子產品依賴關系呈現出來,比如說Log這個子產品被哪些頁面引用,首頁這個頁面引用了哪些具體的子產品(如:FStyleSheet,Log,utils,LinearGradient.web等等),子產品有多大多少個,都可以非常清晰的展示出來。這樣就非常友善對代碼進行優化和處理,并使資料可視化了。

我們現在項目有多大,它的主要代碼組成結構是什麼樣的,它的每一個子產品,每一個依賴,每一個元件size占比多少,都可以進行精确的資料分析。比如說最大的子產品,你為什麼最大,你包含了哪些業務邏輯,是不是必須的。這些資料都可以進行再一步的思考。

CRNWEB目前已經支援到了React-Native的最新版本0.54版本,React更新到16.2版本,已經有衆多頁面更新上線。

最後看看實際項目運作效果對比:

幹貨 | 揭秘攜程三端通用架構中的CRNWEB