<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          美團(tuán)前端研發(fā)框架Rome實(shí)踐和演進(jìn)趨勢(shì)

          共 12346字,需瀏覽 25分鐘

           ·

          2023-08-28 17:15

          ba1ca276a9488f18af0cefdbac7c812e.webp

          總第570

          2023年 第022篇

          cf754ee549813d7a750312b7abb24841.webp

          本文整理自美團(tuán)技術(shù)沙龍第76期《大前端研發(fā)協(xié)同效能提升與實(shí)踐》,為大家介紹了美團(tuán)到店前端研發(fā)框架Rome實(shí)踐和演進(jìn)趨勢(shì)。 具體來(lái)講,本文首先介紹了Rome整體的工程生態(tài)、演變路徑、規(guī)?;?jí)以及工程框架外的開發(fā)輔助工具;第二部分,重點(diǎn)闡述了如何做框架度量和相關(guān)的業(yè)務(wù)實(shí)踐;最后做整體的總結(jié)以及對(duì)工程框架的下一階段的思考。希望能對(duì)大家?guī)?lái)一些幫助或啟發(fā)。
          • 1 背景介紹

            • 1.1 業(yè)務(wù)背景

            • 1.2 技術(shù)背景

          • 2 工程生態(tài)、演變路徑和規(guī)模化升級(jí)

            • 2.1 工程生態(tài)

            • 2.2 演變路徑

            • 2.3 規(guī)?;?jí)

          • 3 框架開發(fā)輔助

            • 3.1 為什么要基于IDE?

            • 3.2 提效率

            • 3.3 提質(zhì)量

            • 3.4 平臺(tái)流轉(zhuǎn)

            • 3.5 文檔提示

            • 3.6 CloudIDE對(duì)接

          • 4 框架度量和業(yè)務(wù)實(shí)踐

            • 4.1 框架度量-核心問(wèn)題

            • 4.2 框架度量-全景建設(shè)

            • 4.3 業(yè)務(wù)實(shí)踐

          • 5 總結(jié)及工程框架趨勢(shì)思考

          1 背景介紹

          | 1.1 業(yè)務(wù)背景

          首先介紹一下業(yè)務(wù)的背景,這里主要從3個(gè)維度展開。第一個(gè)維度是組織維度,在立項(xiàng)之初,恰逢美團(tuán)的多個(gè)事業(yè)群合并,因前端規(guī)模比較大,橫向的流動(dòng)協(xié)同比較多(需要跨部門支持需求,進(jìn)行跨系統(tǒng)協(xié)作等等)。此外,美團(tuán)到店事業(yè)群新人比例比較高,校招和新員工比例很高,我們會(huì)幫助新同學(xué)快速融入團(tuán)隊(duì),需要完成一些較為基礎(chǔ)的開發(fā)工作。 df55cd86dbe6580aa6e0c3ac43c58f27.webp

          第二維度是業(yè)務(wù)維度,美團(tuán)到店業(yè)務(wù)迭代頻次比較高,基礎(chǔ)工程框架不僅要保證交付速度快,同時(shí)還對(duì)質(zhì)量有很高的要求。

          第三個(gè)維度是系統(tǒng)維度,因業(yè)務(wù)周期比較長(zhǎng),到店還存在大量的存量系統(tǒng),需要考慮遷移升級(jí)和重構(gòu)等問(wèn)題,同時(shí)會(huì)有頻繁的系統(tǒng)交接。

          | 1.2 技術(shù)背景

          在Rome整體立項(xiàng)時(shí),我們已經(jīng)準(zhǔn)備好了相關(guān)的基礎(chǔ)設(shè)施,包括發(fā)布系統(tǒng)的收斂、基礎(chǔ)架構(gòu),統(tǒng)一為基于S3(美團(tuán)內(nèi)部存儲(chǔ)服務(wù))加動(dòng)靜分離的技術(shù)架構(gòu),但是上層開發(fā)框架、組件類庫(kù)種類繁多且開發(fā)方式不統(tǒng)一。存在問(wèn)題包括:整個(gè)團(tuán)隊(duì)中人數(shù)比較多,學(xué)習(xí)交接、建設(shè)維護(hù)成本相對(duì)較高,而整體開發(fā)的效率比較低,跨團(tuán)隊(duì)之間的工程能力也很難進(jìn)行復(fù)用等等。

          abdb32245a1de479f452570ccdea93c2.webp

          建設(shè)之初,我們基于純靜態(tài)S3(美團(tuán)內(nèi)部存儲(chǔ)服務(wù))架構(gòu)進(jìn)行前端框架的建設(shè),這源于我們?cè)缙诖罅炕贜ode.js的前后端一體架構(gòu)存在一些問(wèn)題:首先,事業(yè)群早期以中后臺(tái)場(chǎng)景業(yè)務(wù)為主,對(duì)頁(yè)面的秒開、SEO的訴求比較低;其次,當(dāng)時(shí)Node.js生態(tài)基建還沒有那么完善,前端同學(xué)需要做動(dòng)態(tài)擴(kuò)縮容、峰值流量處理等操作,整體的業(yè)務(wù)風(fēng)險(xiǎn)比較高。同時(shí)還存在機(jī)器成本高、開發(fā)人員能力要求高、招聘難度大等問(wèn)題。

          因此,在整體的建設(shè)思路和路徑上,我們不會(huì)建設(shè)類Egg.js這樣的前后端一體的框架;同時(shí)因?yàn)槲覀兊目蚣軐右鉀Q研發(fā)流程不規(guī)范、交付質(zhì)量不高等問(wèn)題,也需要聯(lián)動(dòng)上下游的設(shè)計(jì)研發(fā)、CI/CD等系統(tǒng)形成一體的開發(fā)工程平臺(tái),而不只是做CLI工具。

          2 工程生態(tài)、演變路徑和規(guī)?;?jí)

          | 2.1 工程生態(tài)

          2.1.1 降學(xué)習(xí)成本

          框架約束

          根據(jù)前文所述,我們一開始要解決的核心問(wèn)題是學(xué)習(xí)成本,因此我們會(huì)做框架約束。

          • 第一是工程目錄,我們?cè)谡嬲_發(fā)的時(shí)候,比如UI框架是使用Vue還是React,UI框架本身的語(yǔ)法成本沒有那么高,但是當(dāng)拿到一個(gè)需求時(shí),應(yīng)該在哪兒改,在哪個(gè)位置梳理邏輯,這個(gè)部分的成本比較高,因?yàn)楹芏鄷r(shí)候不同技術(shù)棧的業(yè)務(wù)組織方式不一樣,會(huì)帶來(lái)比較高的理解成本和心智負(fù)擔(dān)。
          • 第二個(gè)是大團(tuán)隊(duì)下工程能力碎片化,團(tuán)隊(duì)間水平比較參差,帶來(lái)的問(wèn)題是團(tuán)隊(duì)之間難以做好、做深工程能力,典型的如SSR、構(gòu)建優(yōu)化等很難跨團(tuán)隊(duì)間復(fù)用,一直在重復(fù)造低門檻的“輪子”。
          • 第三是業(yè)務(wù)工程的配置比較冗長(zhǎng),帶來(lái)的問(wèn)題是工程配置比較耗時(shí),很多時(shí)候存量系統(tǒng)不敢升級(jí),因?yàn)椴恢肋@么長(zhǎng)的配置加了一些東西,是否會(huì)出現(xiàn)比較嚴(yán)重的后果。
          • 最后是公司基建落地,業(yè)務(wù)對(duì)接過(guò)程中非標(biāo)的基建落地實(shí)踐版本非常多;在公司開發(fā)和自己寫小型項(xiàng)目有很大的差異,在公司內(nèi)要解決監(jiān)控、流量、部署對(duì)接等問(wèn)題,需要做周期升級(jí),而這部分如果不進(jìn)行整體收斂,整體規(guī)模化升級(jí)成本就會(huì)比較高。

          Rome在這一側(cè)的解法:

          • 統(tǒng)一目錄認(rèn)知:如下圖右側(cè)所示,在開發(fā)時(shí),對(duì)于部門或者團(tuán)隊(duì)級(jí)別認(rèn)可的規(guī)范,當(dāng)技術(shù)同學(xué)不遵守這套規(guī)范的時(shí)候,在代碼提交時(shí)會(huì)有卡控,當(dāng)然也可以通過(guò)TL審批跳過(guò)。
          • 收斂依賴選型和版本:在業(yè)務(wù)開發(fā)時(shí),從頭進(jìn)行工程框架選型的上手門檻比較高,因此在公司內(nèi)將版本和選型進(jìn)行統(tǒng)一收斂,保證大家可以專注業(yè)務(wù)。
          • 統(tǒng)一跨技術(shù)棧工程能力和開閉配置:保證大家拿到工程后,不管是哪個(gè)技術(shù)棧都可以以一個(gè)相對(duì)一致的認(rèn)知進(jìn)行開發(fā),無(wú)需關(guān)注構(gòu)建、工程能力配置等。
          • 收斂基礎(chǔ)開發(fā)規(guī)范:默認(rèn)在拿到一個(gè)項(xiàng)目時(shí),如果沒有特別定制化的團(tuán)隊(duì)規(guī)范,基本上可以不用關(guān)注像ESLint、Prettier、EditorConfig、部署對(duì)接等開發(fā)規(guī)范。

          33aa1770de3f39d03dcb6ef7a686c8e9.webp

          跨技術(shù)棧開發(fā)認(rèn)知一致

          在實(shí)際迭代的時(shí)候,到店內(nèi)部一開始整體收斂到Vue技術(shù)棧,后來(lái)因團(tuán)隊(duì)合并,又有了React技術(shù)棧。我們采取的第一個(gè)策略是把Vue和React整體目錄保持大家認(rèn)知的一致,如下圖所示。而在真正開發(fā)的時(shí)候,比如配置子目錄SRC下,當(dāng)一個(gè)同學(xué)一開始是偏Vue,但開發(fā)React需求的時(shí)候,即使團(tuán)隊(duì)和業(yè)務(wù)有一些變動(dòng),TA也可以準(zhǔn)確拿到項(xiàng)目快速啟動(dòng),接下來(lái)擺在TA面前的可能就是特定技術(shù)棧下,對(duì)于對(duì)應(yīng)的API理解是否深入的問(wèn)題,這部分同學(xué)自己就可以解決。

          第二是我們會(huì)保證使用Rome開發(fā)框架的整體開發(fā)調(diào)試流程和體驗(yàn)一致。如下圖,在Vue或React框架中同學(xué)專注業(yè)務(wù)開發(fā)就行,基本上可以不用關(guān)注工程框架的相關(guān)事情。比如Vite在Vue或在React指令和表現(xiàn)是一致的,同學(xué)可以通過(guò)一行命令直接開始調(diào)試對(duì)應(yīng)項(xiàng)目并進(jìn)行開發(fā)。

          aa0914872fa19b5ebd5a6285ec154210.webp

          第三,我們的整體工程能力會(huì)進(jìn)行高度對(duì)齊,如上圖所示。我們生態(tài)下80%的插件在Vue和React下,用法和表現(xiàn)完全一致,大家基本上不用關(guān)心這部分,當(dāng)真正用到某部分能力,詳細(xì)看對(duì)應(yīng)的文檔就行。

          基礎(chǔ)基建對(duì)接

          工程能力:一行代碼引入公司基建(CDN容災(zāi))

          當(dāng)前端同學(xué)處理如CDN廠商故障等問(wèn)題時(shí),需要自行查找、學(xué)習(xí)和使用公司/開源的基建能力,解決成本較高,但在Rome開發(fā)過(guò)程中可以通過(guò)開閉式的配置一鍵CDN容災(zāi)生效,如下圖,接入后會(huì)自動(dòng)進(jìn)行靜態(tài)資源的降級(jí)重試,當(dāng)業(yè)務(wù)出現(xiàn)故障時(shí)(即下圖峰值部分),它會(huì)有一個(gè)明顯的資源加載重試,這就是降級(jí)CDN在生效;對(duì)業(yè)務(wù)同學(xué)來(lái)說(shuō)只需配置框架需要哪個(gè)能力就行,其詳細(xì)配置如何注入大家并不需要太關(guān)注。

          7fcaa074b6c2dcda6e7a2f5de96c5f0a.webp

          工程能力:一行代碼引入公司基建(告警監(jiān)控)

          業(yè)務(wù)開發(fā)以外的像告警監(jiān)控,對(duì)同學(xué)來(lái)說(shuō),可能也要理解很多東西。但是在企業(yè)內(nèi)開發(fā),我們希望同學(xué)可以專注業(yè)務(wù)開發(fā),像線上告警、日志鏈路等出現(xiàn)問(wèn)題,可以不用配置對(duì)應(yīng)平臺(tái),我們用對(duì)應(yīng)的項(xiàng)目Key就可以查到對(duì)應(yīng)的錯(cuò)誤總量、錯(cuò)誤調(diào)用鏈路等。

          259d7d2cb90b2a635ee708cb8bbb7f16.webp

          工程能力:一行代碼引入公司基建(水?。?/strong>

          我們也可以一鍵完成對(duì)應(yīng)的水印接入,同時(shí)開發(fā)配置時(shí)有對(duì)應(yīng)的配置項(xiàng)智能提示。各種開閉能力的屬性命名語(yǔ)義可能沒那么清晰,可以通過(guò)如VSCode中的智能提示一鍵跳轉(zhuǎn)到對(duì)應(yīng)能力的文檔地址,查看它的實(shí)現(xiàn)原理以及使用方法。

          5904301e1f88ff92c1a8e5d8c2dad1f6.webp

          2.1.2 降建設(shè)成本

          之前組織下建設(shè)的很多能力是偏小蘑菇的形式,“小蘑菇”的語(yǔ)義是整體工程能力的生命周期比較短,會(huì)進(jìn)行頻繁的重復(fù)建設(shè)。但我們希望以一個(gè)大樹的形式建設(shè),“大樹”的語(yǔ)義是我們可以通過(guò)跨團(tuán)隊(duì)、橫向方式進(jìn)行高質(zhì)量的設(shè)計(jì)和建設(shè),通過(guò)評(píng)審委員會(huì)把控整體的方案建設(shè),把方案拆分到幾個(gè)階段,橫向團(tuán)隊(duì)共建,共同完成核心能力,比如Serverless、SSR等。

          工程能力生態(tài)

          如果大家對(duì)工程框架的建設(shè)能力感興趣,可以參考我們內(nèi)部的效率、質(zhì)量、體驗(yàn)和領(lǐng)域?qū)嵺`進(jìn)行對(duì)應(yīng)分類的建設(shè)。我們通過(guò)一線調(diào)研、能力盤點(diǎn)的方式建設(shè)了31個(gè),在企業(yè)內(nèi)進(jìn)行建設(shè)時(shí),有很多能力一開始可能本身成本就很高,比如統(tǒng)一基建接入等,大概需要 30人力/個(gè),而實(shí)際業(yè)務(wù)需求開發(fā)一共只有幾天時(shí)間,很難說(shuō)服產(chǎn)品運(yùn)營(yíng)單獨(dú)抽出大量人力開發(fā)前端基建能力,這部分我們就以共建+橫向評(píng)審的方式,保證業(yè)務(wù)團(tuán)隊(duì)可以攤平整體的建設(shè)成本。

          73c97ad50ade840654f34c5907fa6d13.webp

          典型共建案例

          Serverless SSR能力,在休娛頻道的密室和優(yōu)選團(tuán)長(zhǎng)端的訂單管理流量分別是百萬(wàn)級(jí)別和千萬(wàn)級(jí)別,整體秒開可以做到90%左右。

          a72fe78912ed88ece0fff53b701bc26c.webp

          整個(gè)過(guò)程是Rome + Arche團(tuán)隊(duì)共建并形成到店標(biāo)準(zhǔn),業(yè)務(wù)統(tǒng)一落地,過(guò)程中有超過(guò)3個(gè)業(yè)務(wù)團(tuán)隊(duì)參與了部分能力建設(shè)。

          這里簡(jiǎn)要介紹下Serverless的方案流程:

          首先是訪問(wèn)自動(dòng)降級(jí),用戶通過(guò)負(fù)載均衡命中對(duì)應(yīng)的網(wǎng)關(guān),命中對(duì)應(yīng)的Serverless的Node Runtime,這里會(huì)有對(duì)應(yīng)頁(yè)面粒度的SSR渲染函數(shù),當(dāng)用戶訪問(wèn)服務(wù)器返回 5.x.x 狀態(tài)碼或超時(shí)時(shí)會(huì)自動(dòng)降級(jí)到CSR資源(SPA頁(yè)面);即使出現(xiàn)了極端情況,比如 SSR 服務(wù)異常,我們也可以保證整體頁(yè)面可用,差別主要在 SSR場(chǎng)景秒開~90%,CSR場(chǎng)景秒開~30%,兼顧了性能和穩(wěn)定性的優(yōu)勢(shì)。

          其次是支持頁(yè)面粒度接入,在大型 MPA 項(xiàng)目開發(fā)中,可以把某個(gè)頁(yè)面單獨(dú)設(shè)置為SSR,不必要求整個(gè)項(xiàng)目都是SSR工程,構(gòu)建時(shí)默認(rèn)會(huì)上傳CSR端構(gòu)建資源,而針對(duì)SSR頁(yè)面會(huì)額外進(jìn)行另外一份資源的構(gòu)建,在部署SSR端、CSR端資源時(shí)分別部署,用戶訪問(wèn)頁(yè)面鏈接時(shí),對(duì)應(yīng)命中Server端或者CDN資源。

          支持自動(dòng)擴(kuò)縮容和流失渲染,對(duì)接公司Nest基建,支持業(yè)務(wù)無(wú)感知的峰值流量自動(dòng)擴(kuò)縮容;同時(shí)長(zhǎng)列表頁(yè)面也可以通過(guò)流式渲染來(lái)降低TTFB時(shí)間。

          b0e1ec95adbb07f98176ca5eae5b74f4.webp

          2.1.3 提研發(fā)效率

          編譯提速主要分兩個(gè)方向進(jìn)行業(yè)務(wù)落地,分別是Webpack體系和Vite體系。

          編譯提速 - Webpack體系優(yōu)化

          首先是Webpack體系,因?yàn)槲覀冋w的技術(shù)棧分Vue和React兩部分,我們會(huì)抽一部分基礎(chǔ)的跨技術(shù)棧的Webpack配置,上層有不同團(tuán)隊(duì)分別維護(hù)Vue技術(shù)棧和React技術(shù)棧的特有配置,中間我們會(huì)進(jìn)行專項(xiàng)優(yōu)化,包括像Webpack5的開發(fā)優(yōu)化、編譯壓縮器、SWC和esbuild等。

          另外,我們做工具時(shí),不僅要考慮我們本身構(gòu)建工具比如Webpack,也要考慮如何和我們的CI/CD平臺(tái)結(jié)合,如緩存復(fù)用結(jié)合依賴安裝、構(gòu)建工具做,在很多需求場(chǎng)景下,可以獲得耗時(shí)的10倍提升,CI/CD側(cè)我們也支持分粒度的構(gòu)建,比如大型MPA做頁(yè)面粒度的構(gòu)建等。

          116ff9f3b1317411ec18fd2fa5075fd9.webp

          編譯提速- 開發(fā)時(shí)?鍵Vite

          Vite主要是我們?cè)陂_發(fā)時(shí)使用的,Webpack是構(gòu)建時(shí)使用,除了少部分增量項(xiàng)目的開發(fā)和構(gòu)建都走Vite,大多數(shù)存量項(xiàng)目都是開發(fā)使用Vite保證效果,構(gòu)建階段使用Webpack。

          9502e2f13b356a020305c988b01d9f19.webp

          整體流程:首先是啟動(dòng),通過(guò)Vite,比如createServer拉起頁(yè)面,抹平Webpack差異;環(huán)境變量的抹平,保證客戶端Webpack存量項(xiàng)目,將Webpack客戶端的環(huán)境變量注入到Vite端;對(duì)配置文件,大家原本在rome.config.js中的Webpack配置也可以一鍵轉(zhuǎn)換為對(duì)應(yīng)的Vite配置;在下層我們會(huì)做內(nèi)置的技術(shù)棧插件,包括Vue2/Vue3 SFC+JSX,包括React的內(nèi)置插件等;比較繁瑣的是生態(tài)兼容,在公司內(nèi)這部分的工作量比較大,像我們的一些SaaS的靜態(tài)資源的一些路徑等,包括之前我們存量Webpack這么多年積累下來(lái)的公司基建,我們?nèi)绾伪WC它能被注入到Vite端,包括組件庫(kù)按需引入,當(dāng)一些模塊比如common.js的模塊如何指向?qū)?yīng)的esmodule目錄等,最后我們會(huì)屏蔽一些認(rèn)知成本,把Vue、React和公司依賴包內(nèi)置到Vite預(yù)編譯內(nèi)容中。

          依賴提速

          主要分兩部分,分別是開發(fā)階段和部署階段。

          首先會(huì)做工程框架自身Node.js依賴的預(yù)構(gòu)建,這部分核心解決的是Node.js端公司內(nèi)外包資源的體積和遞歸依賴數(shù)量龐大的問(wèn)題。依賴安裝的一個(gè)核心耗時(shí)其實(shí)是遞歸依賴龐雜,需要逐個(gè)安裝帶來(lái)的耗時(shí),比如需要安裝A,其實(shí)A依賴了B、C、D、E、F、G,整體安裝耗時(shí)就會(huì)非常久,同時(shí)研發(fā)同學(xué)對(duì)Node.js包體積、依賴數(shù)量一般不如面向?yàn)g覽器端投放的NPM包那么敏感,這個(gè)時(shí)候預(yù)編譯就很關(guān)鍵。

          其次是把整體的依賴環(huán)節(jié)包管理工具切換到Pnpm。這部分經(jīng)過(guò)調(diào)研(Pnpm、Yarn Berry、Yarn Berry with PNP等),判斷Pnpm是未來(lái)幾年的核心趨勢(shì),同時(shí)可以穩(wěn)定發(fā)展。內(nèi)部建設(shè)了對(duì)應(yīng)的包管理遷移工具,來(lái)服務(wù)企業(yè)內(nèi)幾百個(gè)、上千個(gè)項(xiàng)目。

          ecfc0153aed3a6866fd4d0b2ba15ca8f.webp

          在部署階段定制了Rome依賴安裝Docker鏡像,鎖Pnpm版本和內(nèi)置常用依賴、緩存復(fù)用,它的整體邏輯是上一次需求迭代和下一次需求迭代可能依賴包的變化沒有那么多。我們?cè)诘诙伟l(fā)布的時(shí)候,如果能夠嘗試命中上一次的node_modules里的一些包,那第二次安裝耗時(shí)就會(huì)有一個(gè)比較大的提升。

          | 2.2 演變路徑

          第一階段的時(shí)候是強(qiáng)依賴Webpack和Vue技術(shù)棧的。直到2020年-2021年,像社區(qū)UI框架有Solid.js、Svelte、Vue3,構(gòu)建工具有Esbuild、Rspack、Vite等且發(fā)展勢(shì)頭也比較好,面向這個(gè)大趨勢(shì),我們希望研發(fā)框架能夠分層過(guò)渡,不再和具體的UI框架、構(gòu)建工具綁定:

          1. 首先最底層是差異機(jī)制,核心做插件的加載和調(diào)度;
          2. 中間層是構(gòu)建工具的bundler ,這一層主要是分別結(jié)合Webpack、esbuild、Rspack等構(gòu)建實(shí)現(xiàn),支持大量項(xiàng)目;
          3. 上層Plugin、Preset做框架拓展,各個(gè)業(yè)務(wù)能夠基于場(chǎng)景定制自己的工程能力;
          4. 頂層是品牌指令( bin ),這部分主要是企業(yè)內(nèi)合作時(shí)業(yè)務(wù)方希望保持自己的品牌名。

          示例如下圖右側(cè),大多數(shù)情況下,我們有一個(gè)新的研發(fā)框架出來(lái),只需要開發(fā)我們對(duì)應(yīng)的語(yǔ)言特色的插件集合就行,底層插件機(jī)制+構(gòu)建工具大家其實(shí)可以保持一致的。

          10115f8998be543800f730cd97cd0de8.webp

          | 2.3 規(guī)?;?jí)

          框架價(jià)值=覆蓋范圍 * 功能價(jià)值。我們的共識(shí)是建設(shè)的能力需要落地才能產(chǎn)生價(jià)值,核心是幫助大家業(yè)務(wù)低成本地享受好用的能力。

          如下圖所示,是某個(gè)業(yè)務(wù)的升級(jí)成本圖,可以看到有100多個(gè)項(xiàng)目(成本均值~5pd/項(xiàng)目),業(yè)務(wù)面臨幾百天的成本。對(duì)團(tuán)隊(duì)Tech Lead很難說(shuō)服業(yè)務(wù)產(chǎn)品運(yùn)營(yíng)業(yè)務(wù)階段投入升級(jí)。對(duì)一線同學(xué)來(lái)說(shuō),雖然能力很多、很棒,但是升級(jí)風(fēng)險(xiǎn)怎么處理:編譯提升10s,線上問(wèn)題定位消耗一周。

          022dff1cbc056c3206074876fa1a8595.webp

          這部分的我們給兩個(gè)解決方案。

          首先是提供遷移工具(非Rome到Rome項(xiàng)目):

          1. 巨石項(xiàng)目做存量遷移時(shí),一般同學(xué)下意識(shí)反應(yīng)是通過(guò)Babel做靜態(tài)解析遷移,純靜態(tài)解析實(shí)測(cè)的遷移準(zhǔn)確率在40%,也就說(shuō)一個(gè)項(xiàng)目40%的事情通過(guò)Babel可以搞定。
          2. 第二階段是基于構(gòu)建工具的依賴引用關(guān)系去做遷移,通過(guò)構(gòu)建工具啟動(dòng)時(shí)的模塊工廠鉤子去生成依賴引用樹狀結(jié)構(gòu)然后做遷移,得到遷移準(zhǔn)確率在60%左右。
          3. 第三階段是動(dòng)態(tài)分析結(jié)合AST靜態(tài)分析一起去做,在數(shù)據(jù)采集階段通過(guò)構(gòu)建工具的模塊工廠鉤子、Vue/React實(shí)例化對(duì)象的路由/數(shù)據(jù)流配置去生成依賴圖結(jié)構(gòu),通過(guò)recast等工具進(jìn)行靜態(tài)解析,不用Babel是因?yàn)閞ecast有比較好保持樣式的代碼回寫,文件內(nèi)容增刪改后寫回原文件樣式不亂,同學(xué)在git diff時(shí)需要Code Review代碼就可以少一些,便于遷移影響范圍判斷。
          4. 最后配備上基礎(chǔ)公司基建做的一些依賴版本、體積、兼容性檢測(cè)的質(zhì)量工具,自動(dòng)遷移了差不多有100多個(gè)項(xiàng)目,線上零Case。

          07b8dc322d75ba5199ff48c6a1c910f7.webp

          另一個(gè)是做框架生態(tài)的大版本升級(jí),假如一個(gè)季度推出來(lái)一個(gè)大版本,實(shí)際業(yè)務(wù)開發(fā)升級(jí)難度、成本就會(huì)比較大,業(yè)務(wù)可能也不敢升級(jí),這時(shí)大版本升級(jí)就要提供關(guān)鍵流程自動(dòng)遷移支持:

          1. 當(dāng)從Yarn到Pnpm的時(shí)候,會(huì)做幽靈依賴檢測(cè),自動(dòng)向Pnpm/src的幽靈依賴配置等;
          2. 也會(huì)做工程源碼對(duì)比;
          3. 構(gòu)建配置,如Webpack前后框架無(wú)關(guān)的配置對(duì)比;
          4. 產(chǎn)物變動(dòng)升級(jí)前后entry數(shù)量是否不一樣、產(chǎn)物有沒有明顯變大;
          5. 兼容性檢測(cè),升級(jí)前是ES 5+的代碼,升級(jí)后到ES 6了,那也會(huì)出問(wèn)題;
          6. 線上驗(yàn)證等。
          7a46e5b0dd70689b072547c60eb5b365.webp公司內(nèi)部的一些標(biāo)桿團(tuán)隊(duì)都是由我們Rome團(tuán)隊(duì)升級(jí)+寫報(bào)告,標(biāo)桿業(yè)務(wù)由基建團(tuán)隊(duì)升級(jí)的話,ROI可能會(huì)比較高,我們升級(jí)通過(guò)工具將我們的二次調(diào)整,整體成本可能會(huì)非常低,2~4h就可以完成一個(gè)中等存量項(xiàng)目,再由核心業(yè)務(wù)同學(xué)驗(yàn)收、線上灰度跟進(jìn)等。

          3 框架開發(fā)輔助

          我們Rome的工程開發(fā)輔助工具是基于IDE的。一般業(yè)界的開發(fā)輔助工具兩種形式,下圖是Vue UI和VSCode的拓展圖示:

          • 一是通過(guò)Web實(shí)現(xiàn),典型案例有Vue UI/Umi UI/Angular Console等;
          • 二是VSCode拓展,如Ice Works等。

          | 3.1 為什么要基于IDE?

          bc7043d379e62b56102be5cd5497eb27.webp

          理解:對(duì)于Web形式,業(yè)界以Umi UI(更先進(jìn))和Vue CLI為主,雖然它們都是基于Web實(shí)現(xiàn),但出發(fā)點(diǎn)不太一樣,Vue UI核心是做體驗(yàn)提升,支持工程創(chuàng)建CLI可視化,工程配置分析等;但是UMI UI要服務(wù)公司內(nèi)部,更多的是做提效,支持一些組的資產(chǎn)插入、執(zhí)行工程任務(wù)比如build/lint等。

          基于Web做開發(fā)輔助工具

          • 好處:跨IDE的成本比較低,前端同學(xué)覆蓋會(huì)比較全,也可以解決IDE內(nèi)功能交互UI受限問(wèn)題。
          • 問(wèn)題:VSCode不開放這部分的UI侵入能力,一方面心智認(rèn)知類Umi UI的趨勢(shì)其實(shí)把開發(fā)時(shí)的用戶心智切換到Web端。但我們研發(fā)時(shí),其實(shí)要在IDE看代碼、理解代碼、寫代碼,當(dāng)我們熱更新起來(lái)以后,像構(gòu)建、物料插入、Link等其實(shí)比較低頻,就會(huì)帶來(lái)一個(gè)問(wèn)題,我們研發(fā)時(shí)的心智很難切到對(duì)應(yīng)的瀏覽器端。另一方面研發(fā)鏈路,長(zhǎng)期來(lái)看,不管是業(yè)界還是美團(tuán)內(nèi)部的研發(fā)趨勢(shì)中就一部分場(chǎng)景,后續(xù)肯定是用Cloud IDE,但是我們工程框架的Web端這種開發(fā)輔助工具,如果通過(guò)Web形式實(shí)現(xiàn),就很難融入到我們的研發(fā)鏈路中。
          48460575a67a56d0ca9da16fb9d8937d.webp

          IDE側(cè)方案確實(shí)存在UI交互受限、視窗面積、跨IDE開發(fā)成本等問(wèn)題,因?yàn)橥瑢W(xué)在開發(fā)時(shí),VSCode的面積就這么大,每彈出一個(gè)Webview就會(huì)侵占用戶的開發(fā)視窗,會(huì)比較難受。

          選型原因:

          • 用戶頻次:我們核心是企業(yè)內(nèi)做提效,那就決定了繞不開IDE側(cè),而且TA是用戶高頻部分,而且我們?cè)诖a側(cè)也可以復(fù)用VSCode、IDE側(cè)的LS API,交互受限通過(guò)用戶引導(dǎo)是可以接受的。
          • 研發(fā)整合:我們后續(xù)可以在Cloud IDE整體比較成熟的時(shí)候,會(huì)把部分輕量迭代切換到Cloud IDE中進(jìn)行研發(fā),同時(shí)希望新模式下開發(fā)輔助工具可以繼續(xù)產(chǎn)生價(jià)值。第二是可以在IDE側(cè)整合上下游鏈路,保證用戶心智可以一直停留在IDE內(nèi)而不用跨平臺(tái)操作,整體成本可能會(huì)比較高。
          • 快速落地:不管是Vue UI這種全局CLI的形式,還是Umi UI這種跟隨項(xiàng)目的NPM包形式,都有一個(gè)比較大的問(wèn)題是版本碎片化,我們整體Node的生態(tài)社區(qū),NPM包版本的不穩(wěn)定性,對(duì)同學(xué)來(lái)說(shuō)是認(rèn)知比較強(qiáng)烈的一個(gè)事,功能迭代后,讓大家進(jìn)行NPM包版本升級(jí)難度會(huì)比較大,而且同學(xué)會(huì)比較慌,而VSCode IDE側(cè)的進(jìn)行發(fā)布,它是可以進(jìn)行動(dòng)態(tài)版本下發(fā)的,保證我們大范圍落地,這一部分的兼容成本就由我們框架團(tuán)隊(duì)內(nèi)部消化,做一些兼容。

          0f7028242e86a0c9205d059b5394dc65.webp

          | 3.2 提效率

          提效率的一個(gè)例子是“一分鐘內(nèi)部署”,這里主要解決我們高頻的測(cè)試環(huán)境部署流程冗長(zhǎng)問(wèn)題。2021年我們Rome?線上環(huán)境發(fā)布?概10W次/年,核?流程369s/次,如下圖所示:

          69967cb34540b48714f6b442e6e84fb1.webp

          通過(guò)我們的開發(fā)輔助工具,可以做到頁(yè)面粒度的CSR/SSR發(fā)布,同學(xué)不需要關(guān)注跨平臺(tái)之間有哪些工作量,我們會(huì)在背后同步并且流轉(zhuǎn)DevOps節(jié)點(diǎn),把原本我們要在研發(fā)流程中,企業(yè)內(nèi)要做質(zhì)量分析、規(guī)范卡控等部分做異步,然后異步進(jìn)行觸發(fā),整體的鏈路就可以保證一分鐘內(nèi)完成部署。

          | 3.3 提質(zhì)量

          IDE可以做開發(fā)時(shí)質(zhì)量檢測(cè),開發(fā)時(shí)會(huì)有歷史的線上故障提醒。

          哪些問(wèn)題我們之前已經(jīng)出過(guò)很多次線上故障了,在開發(fā)時(shí)就會(huì)實(shí)時(shí)檢測(cè)代碼并提示這部分應(yīng)該進(jìn)行修復(fù),否則后續(xù)可能會(huì)出現(xiàn)線上故障。

          它和ESLint的區(qū)別:

          1. 首先是我們可以做規(guī)則的動(dòng)態(tài)下發(fā),研發(fā)規(guī)則配置,做管理中心化;
          2. 內(nèi)容上我們也會(huì)支持故障包版本,比如業(yè)界之前出過(guò)幾次故障包問(wèn)題,包括core-js、內(nèi)部組件庫(kù)故障版本等;
          3. 我們也會(huì)把歷史業(yè)務(wù)線上故障分析出來(lái)并提示,點(diǎn)擊鏈接就可以跳轉(zhuǎn)詳細(xì)查看之前具體是出了哪次線上問(wèn)題,是由什么引起的。
          2a3caa0243c08cadfad3191ef1a3a84f.webp

          | 3.4 平臺(tái)流轉(zhuǎn)

          我們內(nèi)部研發(fā)時(shí)不僅要開發(fā),還有設(shè)計(jì)稿,包括DevOps平臺(tái)的流轉(zhuǎn)等,也會(huì)上下游聯(lián)動(dòng),保證設(shè)計(jì)可以快速到DevOps平臺(tái),DevOps平臺(tái)可以一鍵打開本地VS Code,記憶歷史的倉(cāng)庫(kù)和分支并快速打開,也可以快速流轉(zhuǎn)到設(shè)計(jì)平臺(tái),開發(fā)完返回剩余流程。

          2aad3f0681fe7093ef349a98abb9e4ea.webp

          | 3.5 文檔提示

          開發(fā)時(shí)也會(huì)給到大家文檔提示,只要研發(fā)人員托管到到店內(nèi)部的知識(shí)庫(kù),就可以在開發(fā)時(shí),自動(dòng)進(jìn)行相關(guān)文檔的匹配和跳轉(zhuǎn)提示。

          42cbfd975d404bd13e53e91e7e628147.webp

          | 3.6 CloudIDE對(duì)接

          得益于業(yè)界IDE標(biāo)準(zhǔn)的高度統(tǒng)一,一般CloudIDE僅需一次開發(fā),即可本地、云端IDE自動(dòng)安裝開發(fā)輔助,能力完全一致。

          8e8a22704d677e0e5f5f1104d68cb776.webp

          4 框架度量和業(yè)務(wù)實(shí)踐

          上文中從接入公司基建、編譯提速、框架大版本升級(jí)等方面提到了很多工程能力,過(guò)程中也建設(shè)了升級(jí)、遷移工具,而作為企業(yè)內(nèi)框架,也有一些問(wèn)題和指標(biāo)需要考慮。

          | 4.1 框架度量-核心問(wèn)題

          問(wèn)題和指標(biāo)

          a6f03a330c884de380aa928108bd8095.webp

          在業(yè)務(wù)規(guī)模不斷擴(kuò)大、能力落地覆蓋范圍及用戶數(shù)量都在不斷增加的背景下,框架現(xiàn)狀衡量、框架如何發(fā)展等問(wèn)題逐漸顯現(xiàn):

          Rome的用戶群體從純B端業(yè)務(wù)到覆蓋B&C端業(yè)務(wù),業(yè)務(wù)節(jié)點(diǎn)已覆蓋4個(gè)事業(yè)群、15個(gè)事業(yè)部;對(duì)比2020年,項(xiàng)目規(guī)模已經(jīng)擴(kuò)大了11倍,公司內(nèi)共落地1000+項(xiàng)目。
          1. 如何判定標(biāo)桿業(yè)務(wù)、框架能力支撐效果,如何持續(xù)快速統(tǒng)計(jì)用戶規(guī)模并做運(yùn)營(yíng)?

          框架價(jià)值=覆蓋范圍 * 功能價(jià)值。能力開發(fā)完我們需要盡可能清晰化并提升落地覆蓋,在統(tǒng)計(jì)運(yùn)營(yíng)上可以通過(guò)CI/CD平臺(tái)發(fā)布過(guò)程數(shù)據(jù)、Git倉(cāng)庫(kù)掃描數(shù)據(jù)、框架運(yùn)行時(shí)數(shù)據(jù)打點(diǎn)、IDE使用數(shù)據(jù)來(lái)做框架能力和用戶規(guī)模數(shù)據(jù)采集;同時(shí)結(jié)合標(biāo)桿團(tuán)隊(duì)、標(biāo)桿業(yè)務(wù)以組織維度框架生態(tài)能力最大提效數(shù)據(jù),并對(duì)能力驗(yàn)證和優(yōu)化有正反饋,促進(jìn)能力高質(zhì)量、更大規(guī)模落地。

          2. 哪些生態(tài)能力使用頻率高,哪些能力使用頻率低,哪些是安裝在項(xiàng)目中但沒有實(shí)際使用的?

          面對(duì)已建設(shè)的生態(tài)能力,如果純從能力開發(fā)視角出發(fā)、缺少有效的衡量體系,會(huì)導(dǎo)致有限的人力會(huì)被分?jǐn)偟椒浅}嫶蟮墓こ腆w系內(nèi):(1)已有的好能力因?yàn)闆]有度量數(shù)據(jù),感知弱,在人員變動(dòng)等客觀背景下沒有得到持續(xù)足夠的推廣導(dǎo)致好能力沒有獲得足夠的落地,導(dǎo)致收益低于預(yù)期。(2)初期能力建設(shè)完后產(chǎn)生持續(xù)的維護(hù)成本,對(duì)部分使用率低的能力無(wú)法感知,未及時(shí)做出關(guān)鍵判斷,產(chǎn)生一部分人力浪費(fèi)。(3)對(duì)存在優(yōu)化空間的能力,由于未及時(shí)拿到使用數(shù)據(jù),不了解落地問(wèn)題,能力未得到持續(xù)驗(yàn)證和優(yōu)化而導(dǎo)致收益不達(dá)預(yù)期。

          3. 框架如何從產(chǎn)品角度量化價(jià)值?

          從人力節(jié)省、質(zhì)量、縮短交付周期等方面建立有效的產(chǎn)品價(jià)值衡量體系,對(duì)齊框架年度輸出指標(biāo)并持續(xù)運(yùn)營(yíng)。

          4. 下個(gè)階段框架發(fā)展方向和重點(diǎn)是什么?

          不變的是要深入結(jié)合業(yè)務(wù)架構(gòu)重點(diǎn)支撐階段核心業(yè)務(wù)場(chǎng)景,緊跟社區(qū)做一些先進(jìn)能力,同時(shí)需要通過(guò)數(shù)據(jù)來(lái)輔助判斷階段重心是提升框架核心能力覆蓋率,還是要建設(shè)新能力。

          5. 框架面向用戶群體的客服成本是多少?如何降低?

          收集客服數(shù)據(jù),沉淀高質(zhì)量的FAQ,保證增量問(wèn)題不被重復(fù)客服,引入AIGC智能客服等方式降低增量能力客服成本。

          | 4.2 框架度量-全景建設(shè)

          解決思路

          統(tǒng)一定義并梳理指標(biāo)來(lái)源,結(jié)合上下游平臺(tái)提取數(shù)據(jù)、對(duì)缺失數(shù)據(jù)采集并持久化,通過(guò)快照等形式完成數(shù)據(jù)層聚合和差異抹平;中間層會(huì)按照定義的大盤指標(biāo)對(duì)數(shù)據(jù)進(jìn)行統(tǒng)計(jì)擬合校準(zhǔn),包括項(xiàng)目覆蓋率、核心能力使用率、接入率、研發(fā)環(huán)節(jié)效率等等;上層會(huì)按不同的業(yè)務(wù)部門、時(shí)間區(qū)段等提供可切換的覆蓋、效能、質(zhì)量等數(shù)據(jù)看板。

          aa047a4ce26a48acaf883e41f9c19f8a.webp

          數(shù)據(jù)來(lái)源層
          • 研發(fā)過(guò)程數(shù)據(jù)持久化:覆蓋研發(fā)全流程( 工程框架內(nèi)、插件內(nèi)、CI平臺(tái)流水線節(jié)點(diǎn)內(nèi)
          • 生態(tài)產(chǎn)品數(shù)據(jù)埋點(diǎn):Rome Works質(zhì)量檢測(cè)功能埋點(diǎn)等
          • Talos、Fedo平臺(tái)數(shù)據(jù)庫(kù)

          數(shù)據(jù)統(tǒng)計(jì)層

          • 多平臺(tái)數(shù)據(jù)Database
          • 關(guān)鍵信息月度快照:支撐多維度看板的數(shù)據(jù)二次處理

          大盤指標(biāo)層

          • Rome大盤指標(biāo)查詢服務(wù):核心包含輸入輸出指標(biāo)的查詢服務(wù)
          • FEDO工程化大盤指標(biāo)查詢服務(wù):框架接入率、平均編譯構(gòu)建效率等指標(biāo)

          交付(各模塊的數(shù)據(jù)看板)

          • 月度數(shù)據(jù)趨勢(shì)對(duì)比
          • 可切換到餐/到店等組織維度的數(shù)據(jù)查詢展示
          • 技術(shù)棧等細(xì)粒度分類的數(shù)據(jù)查詢展示

          運(yùn)營(yíng)模塊

          • 數(shù)據(jù):覆蓋率指標(biāo)的項(xiàng)目列表查詢服務(wù);X1節(jié)點(diǎn)粒度的數(shù)據(jù)統(tǒng)計(jì)服務(wù)
          • 呈現(xiàn):大盤中增加標(biāo)桿團(tuán)隊(duì)數(shù)據(jù)圖表;增加X1節(jié)點(diǎn)的團(tuán)隊(duì)預(yù)期收益統(tǒng)計(jì)查詢服務(wù)

          | 4.3 業(yè)務(wù)實(shí)踐

          整體Rome業(yè)務(wù)實(shí)踐如下圖所示:截止2022年,落地了1400多個(gè)工程項(xiàng)目,資產(chǎn)庫(kù)的數(shù)量有100多個(gè),到店多數(shù)Web項(xiàng)目是用Rome開發(fā),大家打開美團(tuán)App,可以看到到店業(yè)務(wù)H5頁(yè)面都是通過(guò)Rome進(jìn)行開發(fā)的,有B端系統(tǒng)、C端的H5,還有我們美團(tuán)內(nèi)部的React Native。

          b68f2a7483166f89ca7ae81895502182.webp

          5 總結(jié)及工程框架趨勢(shì)思考

          工程框架不止于CLI。要從需求交付的視角做框架,整個(gè)工程鏈路可能涉及到應(yīng)用創(chuàng)建、應(yīng)用認(rèn)證、開發(fā)基建配置、依賴安裝、編碼、Mock、調(diào)試,包括提交Lint、還有Git、Install、Int、Build、Upload、灰度、Publish等等。

          我們要核心關(guān)注高頻的、規(guī)?;?、價(jià)值比較高的環(huán)節(jié):

          1. 依賴安裝環(huán)節(jié),因?yàn)楸镜亻_發(fā)和線上部署都會(huì)使用到,如果這部分是當(dāng)前核心的耗時(shí)點(diǎn)( 發(fā)布總300s,依賴安裝100s ),下個(gè)階段構(gòu)建就可以先放一放,把依賴環(huán)節(jié)的優(yōu)化提上日程;
          2. 開發(fā)編譯環(huán)節(jié),很多時(shí)候開發(fā)環(huán)節(jié)我們提升了50秒,看起來(lái)好像沒有那么高,但我們團(tuán)隊(duì)如果是1000人有幾千個(gè)項(xiàng)目,調(diào)試編譯頻次是很高的,全量落地后規(guī)?;Ч蜁?huì)非常強(qiáng);
          3. 發(fā)布鏈路層面:交付鏈路中涉及整個(gè)研發(fā)的全部上下游,單純做單點(diǎn)的工程優(yōu)化( 如本地構(gòu)建優(yōu)化 )效果不一定好,可能到后來(lái)只是在卷幾秒的差異。部署階段依賴、構(gòu)建緩存復(fù)用可能可以直接降低 10min/次;另外部署時(shí)流水線節(jié)點(diǎn)異步觸發(fā)、測(cè)試環(huán)境聚焦提速快速看到效果,線上再補(bǔ)充管理所需的質(zhì)量分析、卡控等
          4. 鏈路流轉(zhuǎn)環(huán)節(jié):工程框架和上游的設(shè)計(jì)協(xié)作平臺(tái)怎么流轉(zhuǎn)、下游如何和部署平臺(tái)聯(lián)動(dòng),他們的耗時(shí)可能也比較高,這部分也需要關(guān)注,因?yàn)閷?duì)使用我們這套工具的同學(xué)來(lái)說(shuō),不關(guān)注這個(gè)能力是誰(shuí)提供,而是我們?cè)陂_發(fā)需求的時(shí)候,他明顯會(huì)感覺到你們整體提供的這套產(chǎn)品,我用起來(lái)很難受,可能不一定是工程框架部分,單體都不錯(cuò),一起用就是不夠順滑。
          06ec897a0494fddec492f56daeab5b03.webp

          未來(lái)趨勢(shì)(個(gè)人階段視角)

          1. 第一方面是工程框架要做開發(fā)鏈路的深度整合,可以類比業(yè)界的Vercel,我們企業(yè)內(nèi)也可以類比這些成熟的像SaaS產(chǎn)品去做;
          2. 第二方面是此前提到的Rust基建、構(gòu)建切換Rspack/Vite、輔助開發(fā)工具,保證生態(tài)完善,公司內(nèi)可以平滑升級(jí);
          3. AI時(shí)代:編碼Copilot;如何結(jié)合公司內(nèi)AI基建把歷史業(yè)務(wù)模板、組件庫(kù)、資產(chǎn)庫(kù)做提示生成是一個(gè)可以結(jié)合的點(diǎn)( 框架有相對(duì)嚴(yán)格的資產(chǎn)生產(chǎn)結(jié)構(gòu)規(guī)范,可以提升下游編碼時(shí)資產(chǎn)消費(fèi)準(zhǔn)確率 );通過(guò)知識(shí)庫(kù)問(wèn)答降低技術(shù)項(xiàng)目的客服成本等。


          ----------  END  ----------


           推薦閱讀 

            |  美團(tuán)外賣終端容器無(wú)關(guān)化研發(fā)框架

            | 美團(tuán) iOS 端開源框架 Graver 在動(dòng)態(tài)化上的探索與實(shí)踐

            | 美團(tuán)開源Graver框架:用“雕刻”詮釋iOS端UI界面的高效渲染
          瀏覽 67
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品一级无码免费播放 | 青色色网| 久久综合加勒比 | 日本 Ⅴ一区二区三区色情 | 爱草综合|