【轉(zhuǎn)】攜程 Trip.com App 首頁(yè)動(dòng)態(tài)化探索

一慈格、框架

1.1、技術(shù)選型及思考

首先需要明確定位以及邊界蛋褥,我們需要一個(gè)怎么樣的框架去解決存在的痛點(diǎn)临燃。

需要的:

? 由于在首頁(yè)場(chǎng)景使用,高性能和穩(wěn)定性是最基本的要求烙心;

? 為了不跟隨版本發(fā)布膜廊,所以動(dòng)態(tài)性也是要考慮的;

? 為了解決研發(fā)成本淫茵,多端渲染也是需要解決的問(wèn)題爪瓜;

不考慮的場(chǎng)景:

? 不需要處理復(fù)雜的業(yè)務(wù)邏輯;

? 不支持動(dòng)畫(huà)精細(xì)的交互場(chǎng)景匙瘪;

? 不考慮多個(gè)組件的聯(lián)動(dòng)性铆铆;

通過(guò)梳理場(chǎng)景和邊界使得目標(biāo)清晰蝶缀。我們需要一個(gè)跨平臺(tái)支持動(dòng)態(tài)性并且高性能 UI 渲染框架。

站在自身需求的角度薄货,調(diào)研業(yè)界成熟的方案得出的結(jié)論如下表翁都。

React Native:動(dòng)態(tài)性高,但是學(xué)習(xí)成本和性能(加載性能菲驴、頁(yè)面性能)不理想荐吵;

Flutter:谷歌的跨平臺(tái)框架,性能高赊瞬,但是無(wú)動(dòng)態(tài)性先煎;

通過(guò)以上的調(diào)研,我們打算用 Native 解析 JSON + Flexbox 的方式來(lái)作為最終方案巧涧。

這么做有什么優(yōu)勢(shì)薯蝎?

1)學(xué)習(xí)成本低:Flexbox 布局方式被開(kāi)發(fā)廣泛接受(內(nèi)部跨平臺(tái)技術(shù)棧用的多的是 RN);

2)開(kāi)發(fā)成本低:JSON 和 Flexbox(Yoga)都有成熟的高性能可靠的第三庫(kù)直接使用谤绳,加快框架開(kāi)發(fā)速度(在一個(gè)月內(nèi)將框架完成并且上線)占锯;

3)兼容性強(qiáng):Flexbox 完美兼容 Web 端布局的方式,F(xiàn)oxPage 同時(shí)支持 Web 端的 DSL 的輸出缩筛;

4)自定義&擴(kuò)展強(qiáng):由于自研消略,沒(méi)有包袱,可以在設(shè)計(jì)上以最符合我們的場(chǎng)景來(lái)設(shè)計(jì)框架瞎抛;

1.2艺演、架構(gòu)設(shè)計(jì)

如何做好架構(gòu)設(shè)計(jì),可以先了解下 Chrome 是如何完成一個(gè) HTML 到 UI 的輸出桐臊。

那么 Flutter 渲染流程是如何呢胎撤?

通過(guò)調(diào)研沉淀下我們的渲染流程:

各個(gè)模塊的職責(zé)清晰且獨(dú)立:

Downloader:主要負(fù)責(zé) DSL 更新與下載。

CacheManager:顧名思義断凶,負(fù)責(zé) DSL 的緩存管理伤提。

Parse:這層主要是做 DSL 解析,負(fù)責(zé)將 JSON 數(shù)據(jù)組織成節(jié)點(diǎn)认烁,供下層使用肿男。

Layout:此層職責(zé)為將 Parse 模塊解析之后的數(shù)據(jù)計(jì)算布局,生成布局元素却嗡。

Draw:此層職責(zé)為將 Parse 生成節(jié)點(diǎn)設(shè)置 Layout 層的布局信息輸出 Native 視圖樹(shù)并提交系統(tǒng)完成渲染次伶。

遵守職責(zé)化最小原則,每個(gè)模塊被賦予的職責(zé)最小化并且彼此獨(dú)立稽穆,使后續(xù)的維護(hù)冠王,可以將影響范圍限制模塊內(nèi)部,而不影響其他模塊的穩(wěn)定性舌镶,增強(qiáng)系統(tǒng)維護(hù)性柱彻。也使得對(duì)模塊定制優(yōu)化提供基礎(chǔ)豪娜。

下文會(huì)繼續(xù)針對(duì)框架內(nèi)部原理做深入介紹。

1.3哟楷、DSL 的定義

數(shù)據(jù)綁定

想象一下瘤载,在我們?nèi)粘i_(kāi)發(fā)中,往往是數(shù)據(jù)對(duì)應(yīng)一個(gè) UI 元素的顯示卖擅,需要有一定的綁定數(shù)據(jù)機(jī)制鸣奔。

{
    "parentId":"cfb87570-82d8-11e9-811f-0906d1cca8d4",
    "name": "image",
    "props": {
        "url":"{{product.imageUrl}}",
    },
    "extendId": "",
    "conditions": [],
    "type": "trip-app.image",
}

變量的格式為{{變量名}},如以上示例:我們的 image 組件中 url 的屬性被設(shè)置為 product 對(duì)象屬性中的 imageUrl 的值惩阶。

{
    "parentId":"cfb87570-82d8-11e9-811f-0906d1cca8d4",
    "name": "image",
    "props": {
        "url":"{{products[0].imageUrl}}",
    },
    "extendId": "",
    "conditions": [],
    "type":"trip-app.image",
}

數(shù)組取值格式為{{數(shù)組[Index]}}挎狸,如上,我們可以通過(guò)此方法獲取 products 數(shù)組中的第一個(gè)元素的圖片断楷。

事件

在組件觸發(fā)事件的時(shí)侯锨匆,我們希望能做一些自定義的事情,如跳轉(zhuǎn)頁(yè)面冬筒,怎么定義呢恐锣?

{
    "onClick": {
        "name":"router_call",
        "props": {
            "value": {
            "plugin":"router",
            "method":"openURL",
            "args": {
                "url":"{{products.deeplink}}"
            }
            }
        },
        "type":"function.call",
    }
}

以上示例表示在點(diǎn)擊事件中通過(guò) router 中的 openURL 打開(kāi)了一個(gè)新的頁(yè)面。

條件判斷

在某些條件成立才渲染的場(chǎng)景下舞痰,我們也提供了條件判斷土榴,示例:

{
    "props": {
        "hidden": {
            "name":"render-activity",
            "type": 1,
            "props": {
                "items": [
                    {
                        "key":"{{data.showActivities}}",
                        "operation":"eq",
                        "value":"1"
                    }
                ]
            }
        }
    },
    "type":"trip-app.image",
}

如上示例: image 組件是否隱藏通過(guò){{data.showActivities}} =="1"來(lái)控制。

埋點(diǎn)機(jī)制

我們還定義了動(dòng)態(tài)埋點(diǎn)的一些規(guī)范响牛,如示例:

{
    "name": "image",
    "props": {
        "$traceData": {
            "onClick": {
                "eventName":"home.click.deals.item",
                "data": {
                    "url":"{{data.operatingActivities.0.deeplink}}",
                    "type":"operating_activities"
                }
            }
        }
    },
    "type":"trip-app.image"
}

在 image 組件中聲明了點(diǎn)擊事件玷禽,并且把需要的參數(shù),通過(guò) data 字段一并上傳服務(wù)端娃善。

1.4论衍、布局

我們的目標(biāo)是為了解決動(dòng)態(tài)性和多端一致性瑞佩,那么具備一個(gè)完備的布局能力是一個(gè)基礎(chǔ)要求聚磺。通過(guò)調(diào)研,有以下3種方案可選炬丸。

1)自定義:完全自定義一套規(guī)則瘫寝,實(shí)現(xiàn)成本高,布局效率取決于實(shí)現(xiàn)程度稠炬,所以這邊是“中”焕阿,因?yàn)槭亲远x,所有通用性是三者最差的首启,幾乎獨(dú)家專屬暮屡。

2)Web CSS:實(shí)現(xiàn)一套 Web CSS 樣式集,可想而知毅桃,如果實(shí)現(xiàn)這樣的一套系統(tǒng)代價(jià)是極高的褒纲,為了兼容眾多的 CSS 樣式准夷,布局效率必然會(huì)下降,但是此方案通用性也是最佳莺掠,多端共享衫嵌。

3)Flexbox:彈性盒子布局,從 Web CSS 子集發(fā)展而來(lái)彻秆,在 RN 已得到充分證明它的適用性楔绞,由于 Yoga 的存在,讓我們?cè)趯?shí)現(xiàn)成本上得到下降唇兑。

Yoga 是 Facebook 基于 Flexbox 的跨平臺(tái)布局引擎開(kāi)源庫(kù)酒朵,被用于 RN,Weex 等項(xiàng)目中幔亥,也證明了其高性能和可靠性耻讽。

在實(shí)際使用過(guò)程中,Yoga 在 Android 中發(fā)現(xiàn)了一些問(wèn)題帕棉,不過(guò)我們通過(guò)定制源碼完美解決针肥,并且在實(shí)際體驗(yàn)下來(lái) Yoga 完美的勝任了布局的任務(wù)。

一些布局示例:

 "props": {
    "$layoutStyle": {
        "position":"absolute",
        "bottom": "12",
        "flexDirection":"column",
        "marginLeft": "8",
        "width": "100%",
        "paddingRight":"16"
    }
}

單位定義

width香伴、height 等實(shí)際數(shù)字單位定義中慰枕,我們定義了如下的數(shù)字單位。

1.5即纲、DSL 解析

解析層具帮,要做的事情比較簡(jiǎn)單,為了提高性能低斋,并且對(duì)于相同的 DSL 模板蜂厅,只會(huì)做一次解析,之后便會(huì)把結(jié)果做一個(gè)緩存膊畴,以下的流程圖代表著解析流程掘猿。

1.6、視圖構(gòu)建

視圖構(gòu)建相對(duì)簡(jiǎn)單唇跨,通過(guò)解析層解析之后稠通,每個(gè)視圖組件都會(huì)ViewNode節(jié)點(diǎn)一一對(duì)應(yīng)視圖在虛擬視圖樹(shù)中的狀態(tài),包括了視圖布局屬性买猖,視圖屬性等元素信息改橘。

以下為 iOS 代碼示例:

/**
 視圖節(jié)點(diǎn),映射 FoxPage 中的組件
 */
@interfaceFPViewNode : NSObject
/**
 視圖屬性
 */
@property(nonatomic, strong) FPViewAttribute *attribute;

/**
 子視圖
 */
@property(nonatomic, copy) NSArray<FPViewNode *> *children;

/**
 綁定關(guān)系的值
 */
@property(nonatomic, copy, readonly) NSArray <NSDictionary *> *bindValues;

/**
 綁定關(guān)系的值
 */
@property(nonatomic, strong) NSMutableArray<NSString *> *conditions;

/**
 真正的視圖引用
 */
@property(nonatomic, weak) UIView<FPViewComponentProtocol> *view;

/**
 添加的父視圖
 */
@property(nonatomic, weak) UIView<FPViewComponentProtocol> *superView;

@end

在ViewNode樹(shù)準(zhǔn)備好之后玉控,我們會(huì)將樹(shù)傳遞到渲染層中進(jìn)行渲染操作飞主,在渲染層中,根據(jù)ViewNode節(jié)點(diǎn)的類型,通過(guò)代理的方式碌识,從注冊(cè)的組件之中創(chuàng)建出視圖實(shí)例讽挟,配合 Yoga 布局屬性,轉(zhuǎn)換到 Native 視圖的映射丸冕,由系統(tǒng)完成最終的渲染耽梅。

1.7、數(shù)據(jù)更新

在解析渲染完成之后胖烛,關(guān)于數(shù)據(jù)流是怎么處理的呢眼姐?以下是處理流程圖:

為了優(yōu)化性能,我們針對(duì) UI 元素有變化的部分做 dirty 處理佩番,會(huì)觸發(fā) Layout 和 Draw 模塊重計(jì)算和重繪众旗。

1.8、動(dòng)態(tài)更新

動(dòng)態(tài)更新能力是重要的一環(huán)趟畏,在云端更新了頁(yè)面布局或者樣式之后贡歧,App 需要即時(shí)拉取到最新的 DSL 模板。以下是流程中的時(shí)序圖:


需要注意幾點(diǎn):

1)App 打包需要把線上目前可用的 DSL 模板打包進(jìn) App 中赋秀,避免第一次打開(kāi) App DSL 模板未下載的時(shí)候的空窗口現(xiàn)象利朵;

2)版本升級(jí)需要做好數(shù)據(jù)隔離和清除;

3)DSL 最新版本下發(fā)猎莲,需要做好 backup 與異常校驗(yàn)绍弟;

通過(guò)動(dòng)態(tài)更新機(jī)制,改變了我們發(fā)布需要跟隨版本的痛點(diǎn)著洼,有問(wèn)題樟遣,修復(fù)之后可以直接下發(fā)到用戶的 App。

1.9身笤、可視化頁(yè)面搭建平臺(tái)

看到這里的看官豹悬,心中肯定會(huì)有疑問(wèn)?你們提供變量液荸、布局瞻佛、事件、埋點(diǎn)追蹤莹弊,條件搓逾,手動(dòng)來(lái)寫(xiě)這很模板復(fù)雜度肯定是很高的哀九,不是普通人可以勝任的吧。

是的拜马,我們也意識(shí)到了此問(wèn)題考抄,所以配套了一套可視化的編輯界面细疚。如下面示例圖:

左邊是可視化編輯頁(yè)面,右側(cè)為實(shí)際在 App 場(chǎng)景的使用效果川梅,可以看出還原度還是很高的疯兼。

屬性編輯界面:

二然遏、頁(yè)面工程化的轉(zhuǎn)變

通過(guò)動(dòng)態(tài)化的轉(zhuǎn)變之后,首頁(yè)的業(yè)務(wù)需求開(kāi)發(fā)的工程模式與研發(fā)流程也由此發(fā)生變化吧彪。

在舊模式下待侵,研發(fā)人員更加關(guān)注業(yè)務(wù)需求如何實(shí)現(xiàn),首頁(yè)的業(yè)務(wù)需求如何在已有的框架體系之內(nèi)跑起來(lái)姨裸。新模式下秧倾,研發(fā)人員更注重的是,業(yè)務(wù)組件如何設(shè)計(jì)傀缩,如何完成的一個(gè)高質(zhì)量的業(yè)務(wù)組件那先。

將研發(fā)人員關(guān)注的復(fù)雜度從面降為點(diǎn),使得首頁(yè)的各個(gè)業(yè)務(wù)模塊之間的獨(dú)立性更高赡艰,以及更加穩(wěn)定售淡。模塊之間的復(fù)用性提升,如其他業(yè)務(wù)部門(mén)也想用廣告組件慷垮,他們只需要在其頁(yè)面做些簡(jiǎn)單的配置揖闸。

在組件生態(tài)不斷補(bǔ)充的未來(lái),各個(gè)業(yè)務(wù)線之間共享彼此的組件模塊料身,想完成一個(gè)新的業(yè)務(wù)或者產(chǎn)品楔壤,只需要像樂(lè)高積木一樣堆砌即可。

三惯驼、構(gòu)建業(yè)務(wù)運(yùn)營(yíng)閉環(huán)

在提供技術(shù)基礎(chǔ)的條件下蹲嚣,我們繼續(xù)思考技術(shù)和業(yè)務(wù)之間的關(guān)系,如何將業(yè)務(wù)價(jià)值最大化祟牲,UI 搭建可以通過(guò)平臺(tái)搭建隙畜,是不是可以把產(chǎn)品運(yùn)營(yíng)同學(xué)也一起參與進(jìn)來(lái),構(gòu)建一個(gè)業(yè)務(wù)運(yùn)營(yíng)閉環(huán)说贝。

1)產(chǎn)品運(yùn)營(yíng)同學(xué)提出需求议惰;

2)研發(fā)人員介入需求開(kāi)發(fā),開(kāi)發(fā)組件乡恕;

3)組件搭建業(yè)務(wù)上線之后言询,一站式追蹤線上業(yè)務(wù)價(jià)值;

4)根據(jù)平臺(tái)的數(shù)據(jù)來(lái)實(shí)時(shí)進(jìn)行運(yùn)營(yíng)策略傲宜,如修改頁(yè)面模塊运杭,下線模塊,添加模塊等等函卒;

5)然后反推產(chǎn)品同學(xué)提出更合理的產(chǎn)品需求辆憔;

如何優(yōu)化鏈路,科學(xué)的運(yùn)營(yíng)體系構(gòu)建運(yùn)營(yíng)業(yè)務(wù)閉環(huán)也是重中之中,并且未來(lái)會(huì)持續(xù)不斷在此方向上探索虱咧。

四熊榛、總結(jié)

FoxPage Native 平臺(tái)上線短短 2 個(gè)月,承載了 30+ 次的業(yè)務(wù)調(diào)整腕巡,5+ 次臨時(shí)調(diào)整埋點(diǎn)的需求玄坦,這是之前做不到的。并且合理系統(tǒng)的設(shè)計(jì)也增加了框架穩(wěn)定性绘沉,上線至今無(wú)任何異常發(fā)生营搅。在保證的高質(zhì)量的交付的同時(shí),也大大減少我們研發(fā)成本梆砸,如一個(gè)復(fù)雜的展示模塊開(kāi)發(fā)转质,從原來(lái)的雙端 4 人日降低到雙端 1 人日。

在首頁(yè)動(dòng)態(tài)化的探索中帖世,遇到了很多的挑戰(zhàn)休蟹,也有很多收獲,后續(xù)有很多的功能和需求還需要繼續(xù)優(yōu)化和完善日矫,并且需要考慮更多的場(chǎng)景支持赂弓。我們相信這是一個(gè)好的開(kāi)頭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哪轿,一起剝皮案震驚了整個(gè)濱河市盈魁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窃诉,老刑警劉巖杨耙,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異飘痛,居然都是意外死亡珊膜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)宣脉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)车柠,“玉大人,你說(shuō)我怎么就攤上這事塑猖≈竦唬” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵羊苟,是天一觀的道長(zhǎng)塑陵。 經(jīng)常有香客問(wèn)我,道長(zhǎng)践险,這世上最難降的妖魔是什么猿妈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮巍虫,結(jié)果婚禮上彭则,老公的妹妹穿的比我還像新娘。我一直安慰自己占遥,他們只是感情好俯抖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著瓦胎,像睡著了一般芬萍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搔啊,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天柬祠,我揣著相機(jī)與錄音,去河邊找鬼负芋。 笑死漫蛔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旧蛾。 我是一名探鬼主播莽龟,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锨天!你這毒婦竟也來(lái)了毯盈?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤病袄,失蹤者是張志新(化名)和其女友劉穎搂赋,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體益缠,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厂镇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了左刽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捺信。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖欠痴,靈堂內(nèi)的尸體忽然破棺而出迄靠,到底是詐尸還是另有隱情,我是刑警寧澤喇辽,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布掌挚,位于F島的核電站,受9級(jí)特大地震影響菩咨,放射性物質(zhì)發(fā)生泄漏吠式。R本人自食惡果不足惜陡厘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望特占。 院中可真熱鬧糙置,春花似錦、人聲如沸是目。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)懊纳。三九已至揉抵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗤疯,已是汗流浹背冤今。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茂缚,地道東北人辟汰。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像阱佛,于是被迫代替她去往敵國(guó)和親帖汞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容