阿里跨端技術演進中的實踐與思考

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"編輯 | 馬紅偉"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"嘉賓 | 張舒迪"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2020年12月,QCon全球軟件大會2020·上海站上阿里巴巴張舒迪(聖司)分享了《阿里跨端技術演進中的實踐與思考》,他從跨端技術背景及演進歷程、阿里跨端業務現狀及思考、跨端技術方向思路演進以及對跨端技術未來展望這四個方面進行了深入的分析,從實踐出發爲跨端技術開發者帶來更多思考方向。本文根據此次分享整理。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"一般來說,跨端技術有4類場景,分別是跨設備平臺(跨Web端和手機端)、跨操作系統(如跨安卓和iOS)、跨App以及跨渲染容器。本篇文章將重點圍繞移動領域的跨端技術進行深入探討。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"移動跨端的重點在四類問題的解決上。首先在性能上,如何通過前端和客戶端的結合,實現更優的渲染性能以及交互性能;其次在動態性上,客戶端怎樣能夠實現更低成本的發版、甚至不發版直接動態更新代碼;第三是研發效率,如何提升客戶端動態調試等研發效率;最後就是一致性,如何實現一份代碼的多端部署,如何保證代碼在多個客戶端內展示形態的一致性、杜絕兼容性問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"爲什麼說移動跨端開發需要“子集規範”?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/76\/763ae6a12db5f93f4390618f718f1b8e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"從2008年蘋果發佈第一部 iPhone 手機到現在,移動互聯網已經發展了十幾年,國內移動端領域也經歷了諸多技術演進的階段。在2012-2013年,國內最早的移動互聯網出現,這個階段重點解決了通過一份 PC 代碼如何在 H5 的瀏覽器裏實現渲染。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"2014年,隨着移動互聯網的網民數量以及設備數量的爆發式增長,移動開發步入到 Hybrid 技術階段,重點解決了“互聯互通”、“H5 如何與 Native 能力齊平”以及“加載性能差”這三大問題,這一階段的真正價值是在 Hybrid 加持下,前端技術成爲移動互聯網時代技術選型之一。隨後出現的同層渲染以及 Native 容器化解決的核心問題都是渲染性能差。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"再往下小程序開始出現,比起一個從技術角度出發的解決方案,它更像是一個通過技術來實現的產品或者說商業思路。這個階段在跨端技術發展上是一個很核心的關鍵點,小程序的出現讓業界意識到,我們需要一個能夠幫助自己抹平不同容器技術方案的框架,阿里、騰訊、字節跳動紛紛開始佈局小程序開發,由此誕生了大量的像 Taro、Uni-App 等一系列跨端解決方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"再往下一個階段走,就是現在我們聊跨端技術聊得最火熱的一個話題,也是跨端技術絕對的新貴——Flutter。Flutter 給前端帶來的最核心思路即提供了自繪渲染技術,可以實現兩端完全抹平,它彌補了像 React Native 或者 Weex 通過原生渲染帶來的雙端不一致以及兼容的成本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以上是一個通用的跨端技術演進歷程,從阿里本身來講,以飛豬爲例,在Hybrid的階段做了Claims,在Native容器化階段引入了Weex,在小程序化階段引入了Rax,目前在做Web技術跟Flutter底層引擎渲染能力的對接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"通過調研阿里內部三四十個業務單元當前的業務現狀,我們發現了"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"四個主要的跨端技術問題"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"技術演進訴求 vs 存量業務支持"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":":端穩定性治理高風險,業務開發人員高門檻,歷史包袱逐步鎖死技術棧;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"大前端協同需要 vs 差異化視角與節奏"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":":框架與容器演進都會對上層業務研發帶來影響,節奏不一致導致反覆重構,業務開發與架構地位不對等;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"多端多場景投放 vs 跨端方案割裂"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":":跨端技術的碎片化演進反而帶來了跨端場景兼容適配的高成本問題;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"業務迭代效率 vs 技術協同成本"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":":溝通成本、研發成本、調試成本、招聘成本、管理成本"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"展開來講,第一個問題,通過調研我們發現其中有91.7%的業務單元需要把業務投放到多個客戶端裏去,比如說同時投放到手淘、支付寶以及飛豬團隊,這種情況下會導致這些團隊的95.8%的業務需要適配多個渲染容器,這會帶來什麼問題呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以一組數據來說明,阿里內部各業務單元 App 平均接入和維護 3.15 套跨端⽅方案,對應 2.75 套研發框架,這些數據產生的直接原因,就是因爲我們沒有辦法把上一代產出的業務代碼進行快速遷移,然而那部分代碼也需要在線上進行持續維護,隨着時間的發展以及跨端技術的演進,就導致"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"一個App裏接入的跨端技術方案越來越多、越來越冗餘,繼而帶來“歷史包袱鎖死技術棧”的嚴重問題"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在講第二個問題之前,前端同學需要思考一下什麼時候會重構代碼?一般來說是架構升級時,比如說需要用casey實現MVC的架構、React出現了單項數據流、Vue碰到了MVVM,這種時候往往會選擇重構代碼,在移動互聯網時代卻不一定是這樣,容器技術演進也可能會導致前端的重構代碼。舉個例子,當Weex1.0版本強綁了Vue的框架,如果業務單元是React的技術棧,也需要切到Vue上,換言之Flutter也是如此,Flutter 引擎上實際是強綁了Dart framework,如果要遷移就需要把代碼都遷移到Dart上去,這就導致了"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"不管是做小程序還是客戶端進行同期升級,都需要進行重構"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。從飛豬的歷程來說,發展的6年間平均一年重構一次代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"再來看第三和第四個問題,在沒有Native端的情況下,阿里各個業務單元的業務平均需要投放到3.77個App、適配2.54個渲染容器、編寫2.45套代碼,這反映出來一個現狀,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"隨着跨端技術的演進,不同的技術方案散列在不同的容器裏,技術選型的不一致帶來了大量的跨端問題"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",最後即使能夠用資源冗餘的方式去開發這麼多套代碼,從今天業務研發的視角,每一個問題都需要找到對應的容器協作團隊,對接N個架構組,溝通協調成本之高可想而知。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"那麼是不是存在一個簡單的解決方案,比如說提供一套統一的跨端解決方案,不再通過升級來解決這個問題?答案當然是否定的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"實際上,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"跨端技術解決的是H5代表的研發效率和動態性以及App代表的性能體驗之間找平衡點的問題"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",每一個解決方案它能覆蓋的寬度是有限的,如果這兩個端點之間的距離太長,就沒有辦法提供一套方案來解決所有的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"那麼兩個端點之間是否可以拉近?從前端角度來看,性能體驗會不會變得更好?"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在2014、2015年阿里剛開始做H5的時候,我們還相信隨着硬件技術的提升手機性能問題將迎刃而解,但隨着時間和技術的發展,新的問題滋生出新的性能優化。實際上,在摩爾定律下拿到的性能紅利會被優先用於解決App的續航問題,此外,在流量見頂的情況下,每個業務都會傾向於以更多的功能來獲取更多的用戶,業務的複雜度將會對沖掉硬件升級帶來的性能紅利。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"那從客戶端的角度來講,未來是不是有更好的研發效率和動態性來解決這個問題?答案也是否定的。研發效率問題可能能夠解決,但是客戶端的動態性問題卻解決不了,因爲想要解決這個問題就要動到OS廠商最核心的利益,蘋果不會接受所有應用脫離iPhone手機市場進行自由更新。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"爲了解決這個問題,我們先來重新審視下整個跨端技術的研發。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"一個頁面的開發由三個核心部分組成——研發框架、渲染容器和配套設施。研發框架更多是前端主導,意味着設計模式、MVC、MVVM單項數據流,渲染容器一般由客戶端主導,代表了性能和體驗,最後是代表着研發效率的配套設施。在當前的跨端技術演進中,這三者一直都是強綁的關係,一套新的跨端解決方案一定會強綁一套研發框架、一個渲染容器和基礎設施。思考一下,通過一些方法能否將三者進行解耦,未來客戶端的容器和框架得以實現自由更新與演進,業務也可以同時並行跑在更多的容器中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"從Web思路來想這個問題其實很好解,我們把解決方案想象成瀏覽器的渲染引擎,把業務想象成前端頁面,實際上就是當年瀏覽器大戰時的問題解決思路——標準化組織(W3C\/WHATWG)。在標準化模式下,所有的前端業務和容器端都只需要對應一套統一標準,這樣就把一個O(M*N)的複雜度問題降低到了O(M+N)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/14\/1432cf86f0e496f6b8e1e699d804f1f0.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"以類似的思路去思考,阿里內部所有的渲染容器是不是都可以以統一一套子集規範的方式去做協同,跟此前Weex、React Native建立Web標準的借鑑思路不同,這是提前制定一套能夠讓各個容器去做適配的標準。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"在這個標準之上,我們能夠去統一研發框架,支撐上層更多的業務,那這個思路能否解決前文中的幾大問題?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"第一個是存量業務如何遷移的問題,如果說所有的業務都是基於一套統一的標準來實現,底層容器更新後業務可以平滑遷移,就不存在歷史包袱了。第二個問題是當一份代碼裝在多個App裏時如何降低複雜度,如果說每個App的容器都提供了一套標準化的接口,一份代碼自然可以無縫的裝到各個容器裏去。第三個是協同導致的成本問題,m個容器跟n個業務的同學需要穿插着去溝通交流,試想如果有一個標準化小組作爲中間方,m個業務只需要對標準化小組提交兼容性問題即可,所有的容器也可以根據標準化小組給的排期去做統一的處理,各方都能有一個比較好的結果。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"如何實現一個高性能的標準子集?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/8b\/8ba26c9a9e5de60e3077da2fbebbbdbb.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"如上圖所示,基於阿里跨端技術的發展,下文將會從Web子集標準建設、Web on Flutter建設思路、統一跨端研發框架與跨端配套基礎設施這四個部分入手探討跨端技術方向演進思路。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Web子集標準建設"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"爲什麼是建Web子集標準而非全集,因爲全集太複雜了。通過CanIUse和MDN,我們拉取了目前前端所有的標準規範,包括HTML的元素屬性、Web API規範\/接口、CSS屬性\/僞類\/僞元素、標準事件等,加起來有超過2000個的規範要適配,並且每個規範都有非常複雜的業務邏輯,所以沒有辦法去實現全量。選擇一個標準化子集能夠解決什麼問題呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"首先是能夠控制實現的成本"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",容器同學優先去實現關鍵屬性,ROI一定是最高的。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"其次是能夠優化渲染管線"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",經過20年的優化,Web(或瀏覽器內核)的渲染其實並不慢,但前後標準的無縫兼容帶來的複雜度成本卻很高。Flutter創始人Eric在一次採訪時就曾提到,Flutter誕生的主要靈感就是在做Chromium的性能優化時,他發現把Chromium的性能渲染管線上無關緊要的渲染步驟都去掉之後,瀏覽器的渲染性能提升了20倍,這讓他意識到一個精簡的渲染管線能夠鑄造更好的渲染性能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"第三是兼容Web標準。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"既然是一個Web標準的嚴格子集,那也就是說業務可以無縫遷移到以Web View作爲承載,這有兩點好處:首先,如果渲染容器、客戶端不再進行維護,業務可以隨時下線,前端可以無縫降級到Web View;其次,代碼將可以在端外通過瀏覽器或者說Web View來承載,以此實現多投。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"第四是複用最佳實踐。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"最佳實踐既包含狹義的前端研發框架、前端 NPM 生態,也包含廣義的前端人才、前端培訓教程等知識儲備,我們需要提供給每一個容器進行內部擴展的實例方法,比如優酷App有強管控、處理視頻的需求,它可以在自有容器內做渲染規範,以此實現自有業務在端內更好的體驗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"那如何實現一個高性能的標準子集?標準要如何來定製?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/6b\/6b43be4856f3d800f5e59aba724a31d5.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"從阿里的實踐來看,首先拉通了集團提供渲染容器和前端框架比較大的幾個業務單元,如淘寶、釘釘、支付寶等,從中選擇架構師同學組成一個標準化的工作小組。接下來觀察整個阿里經濟體內的前端業務代碼倉庫,將其使用到的Web API、CSS標籤製成頻度列表,根據頻度高低進行選取,每個屬性都會帶來性能開銷及研發支持成本,最後由工作小組內的資深架構師同學進行評估,以此來決定子集標準。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在標準的制定流程上阿里參考了W3C的標準規範,包括髮起流程、評審流程、草稿標準、實現標準等。目前CSS已覆蓋98條標準,Web API實現了260條標準,HTML仍在梳理過程中。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Web on Flutter 建設思路"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"制定完標準之後就要去看底層的渲染容器了。在標準容器實踐上,下文將會以Web標準如何對接Flutter渲染容器這個思路進行相關闡述。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/de\/dee545d28db51610a809cea3078d4e5e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"Flutter的底層實際上是一個C\/C++寫的引擎層,基於Skia實現了自繪渲染。上層是一個Dart Framework,裏面既包含了響應式能力,也包含了萬物皆Widget的一個實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"那怎麼來對接?先來看一下Flutter在大前端中可能的位置,上圖中第2列是Flutter Native的一個渲染鏈路,上層走Dart的業務代碼,然後過Dart Framework,最後承接在Flutter引擎上。左側的第1列實際上是Flutter官方提供的Flutter for Web,雖然也是對接Web,但思路不同,關鍵點是如何讓Dart代碼裝在我的瀏覽器裏。這裏提供了HTML mode跟WebGL mode這兩類解決方案,前者更多使用HTML標籤、CSS來進行模擬,存在很大的性能問題,已基本廢棄;後者是基於WebGL加上Simbody來實現的一套訓練能力,也是目前正在嘗試的方案,缺點是無法做業務存在,關鍵的能力是當客戶端實現以Dart爲業務代碼的頁面出現無法渲染的情況時,使用這套方案可以實現無縫、快速做兜底。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"第3列是前端視角,打通前端跟Flutter 引擎的對接。最上層的 JSFramework是我們常用的React\/Vue\/Rax,最底層是我們想要複用的Flutter 引擎,實際上中間可能還需要一層Framework,而標準子集就在這一層的 Framework跟上層前端的Framework之間來實現,以此實現一套統一的Web API。最右邊的這一列是傳統的Web渲染鏈路,這一塊就不再贅述了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"整體的技術佈局瞭解之後,下面講一下具體要如何對接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ab\/ab3978b0987296051fc61d52b2151451.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"Flutter渲染與前端渲染有很多相似之處,核心就是Widget Tree、Element Tree、Render Object Tree"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",其中Widget Tree和Element Tree可以理解成一個基本等同的數據結構,差異點是Widget Tree更多是配置信息,面向的是Dart業務的開發同學,是一個比較精簡的數據結構,Element Tree則是框架內部真實實現的實力節點,它裏面既包含了Widget跟Render Object的實力,同時能夠在 WidgetTree到Element Tree之間實現diff算法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"Render Object Tree類似前端的render區,作用是實現具體的渲染。在Render Object Tree這層會進行Layout(佈局)與Paint(繪製)的動作,形成一個Layer Tree扔給Skia庫,即底層Flutter引擎,最終渲染出頁面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"試想一下,前端代碼對接到這三棵“樹”的任意一棵,就能夠實現前端跟Flutter引擎的對接,從這個思路出發業界已經有很多解決方案,下面簡單列舉四個:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"思路1:遵循 Flutter 思路,對接 Widget Tree"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"遵循Flutter思路,不同的是用JS來實現整體Widget Tree,可以理解爲原本由Dart寫的業務代碼在JS裏進行了1:1的實現。這一思路有三點優勢,首先是依託JSC\/V8 使 Flutter 具備了動態化能力,其次其改造成本相對可控,第三是可複用部分 JS 生態(工具庫)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"同時,這個思路也存在三點需要注意的問題,首先,在JS裏去寫Widget Tree不是在寫前端,無法對接前端的標準,因此也喪失了Web視圖的開發特性;其次,JS部分邏輯較重,性能相對也會較差,第三,實際在JS Call對接過程中,會出現JS到C++再到Dart再到C++的一個通信流程,通信開銷成本也會相對較高。在具體實踐中,騰訊的MXFlutter就是沿用了這一思路,更多目的是爲了解決Native的動態性問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"思路2:遵循Web思路,對接WidgetTree"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"這一思路同樣是對接Widget Tree這一層,不同的是在Dart Framework裏去實現Dormitory跟CSSOM,然後再把Dormitory跟CSSOM解析成一個Widget Tree。以一個商品的背景圖片爲例,它可能由Container、Decorationimage、Networkimage三個Widget組成,通過Widget去匹配CSS和HTML標籤的方式,把 Dormitory映射成Widget Tree。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"這一方案存在三點優勢,它既能夠實現跟Web生態的對接,改造成本也相對可控,同時因爲Widget Tree本來就是Flutter暴露給上層開發者的數據結構,不會輕易去改動API,穩定性比較好,未來Flutter升級也可以做平滑的遷移。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"但是它也有兩點問題,首先是會出現兩次diff消耗,前端的研發框架基本上都會有一層diff,但在Widget Tree到Element Tree的轉化過程中還會產生一層diff,這兩層diff實際上是冗餘的,將會導致一定的性能開銷。其次,Widgets的組合完全沒有CSS靈活,衆所周知,前端標準裏並不限制CSS組合,怎樣都不會影響渲染,但在Flutter設計裏有一些Widget是明確不能組合的,這就導致一些CSS屬性的組合不可能通過拼裝Widgets的方式實現,存在一個完備性問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"這套解決方案在業界使用相對比較廣泛,阿里內部有飛豬的Flugy方案,外部有字節的 ORYX方案,還有一套是微信小程序本身的Flutter方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"思路3:遵循 Web 思路,擴展對接 RenderObject"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"第三個思路是越過了Widget這層,直接在Render Object這層去做對接,這種方案就相當於還是在Dart Framework裏面去實現WebAPI、CSSOM,在後面的類似於前端渲染Render Tree的步驟,將其渲染成Flutter 的Render Object Tree。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"這一方案的優勢有兩點,一是去除 Dart Framework diff 流程,性能得到了一個提升,二是直接對接到 Dart Framework的Rendering層,樣式實現自由度變得更高了,可以實現與所有的CSS屬性無縫對接,完備性問題迎刃而解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"同時這一方案也存在兩點劣勢,首先,其改造成本較Widget層對接更⾼,其次的話, Rendering 這一層在Dart Framework裏處於中間位置,也就是說當使用了Flutter內部的數據結構,未來Flutter升級時中間的 API完全有可能變更,將導致升級成本變得非常高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"思路4:遵循 Web 思路,直接對接 引擎 層(自繪版 RN\/Weex)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"這一思路是直接對接C++的 Flutter 引擎,當然中間的Framework是不可缺失的,將Flutter 引擎暴露的能力轉化成前端的Web API。既然要實現 Framework,自然不需要依賴任何的Dart能力了,可以直接把Dart摘除,用C\/C++來實現。這一方案存在三點優勢:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"直接對接最底層的Flutter 引擎層,穩定性較好"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"去除 DartVM 及 Framework,包體積大幅縮減"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"JS可以直接跟C++通信,跨語言通信複雜度降低,性能提升"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"劣勢則是手勢、事件、繪圖這些API都需要自行用C++重新去寫一遍,複雜度跟成本相對較高。這套方案目前阿里內部淘系技術部正在嘗試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/75\/754f6c994b785ba8a777ec8bb99cef74.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"從一個前端開發者的視角來看,其實我們真正需要關注並不是底層Web跟Flutter標準的對接到底該採用哪一套的方案,更應關注的是這套方案能不能對接到子集標準。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在對接到子集標準的情況下,這些方案之間能夠實現平滑遷移,這樣無論是用Dart,還是JS,抑或是C++來實現,在Widgets、Rendering、Flutter 引擎哪一層切都沒有問題。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"統一跨端研發框架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"從阿里內部來看,在 W3C 子級標準之上建立的研發框架,類似像Rax或者Remax之類的跨端方案,雖然也提供了對接底層多個渲染容器的能力,但是對於每個渲染容器都是需要重新打包的,複雜度和維護成本非常高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"在考慮到整個集團內使用場景覆蓋度以及目前覆蓋業務單元數量的情況下,阿里選擇了Rax作爲統一研發框架和標準進行推進。憑藉着Driver Spec機制,Rax同時支持了Webview、Weex、AJX、Flutter的渲染能力,具體的實現方式可以理解爲Rax框架底層定義一套抽象的API結構,每一個底層引擎只需要去適配Drive,當設定的API全部實現就可以驅動上層框架在多個渲染容器中運行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"但這也存在一個問題。底層渲染容器雖然基本都是遵循Web的標準設計,但是大多標準都是不統一的,這就導致一個很關鍵的問題,即最終可能只能選取子集標準作爲適用方,如果子集非常窄,那研發將變得非常受限。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/23\/233ff389d574e7fd7994b33fc471edaf.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"上圖是小程序的研發鏈路,目前Rax採用的是編譯式的方案,通過構建時編譯的方式將Rax DSL直接編譯成axml,但由於Rax的DSL跟小程序的DSL都是非完備的,當把一個非完備的DSL轉成另一個非完備的DSL一定會有很大的限制,這就導致開發中很多特性無法使用,比如說如果用一套Rax代碼編一個小程序,就不能寫高階組件,否則編譯失敗。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/44\/442555ee9d6ea5a7c5aa7aa97baf9b9e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"爲了解決這個問題,阿里把小程序的對接方案改成了一個運行式的方案,把底層小程序提供的API封裝成一套 Web標準子集——可以理解成在小程序的框架之上提供一套標準規範的封裝,在這之上Rax代碼就能夠通過driver-dom,實現上圖這樣一條鏈路。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"再往前想一步,當"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"把標準化容器統一到一套Web API上時,所有業務開發都對接到子集標準,對於Rax這套研發框架,未來可能一次構建其中一套driver-dom就可以實現全平臺的一個適配"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。同時子集標準可以隔離前端的研發框架和底層渲染容器,未來有望對接更多的研發框架。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"跨端配套基礎設施"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"如何保證標準實現的有效性?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"解決這一問題的思路是自動化測試,包含UI自動化、API測試以及性能、穩定性相關指標。阿里的自動化測試框架能夠向上層提供數據,幫助發現當前的容器標準的問題和現狀,然後敦促各個容器的接入方去做改進,同時這些數據也支撐了前端同學的CanIUse以及IDE Plugin,幫助他們提升研發效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"在底層渲染容器不統一的情況下,如何拉齊不同渲染容器之間的性能指標?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"阿里的解題思路是提供兩個核心的時間點,一個是白屏時間(在首頁進行渲染操作的時間點),一個是首屏渲染時間(渲染過程中首屏所有大圖片首次加載完成的時間點),參考了 W3C-LCP 、UC-U4的標準,標準只是一個體感指標,本身跟底層實現無關,它只是一個體感指標,我們可以讓各個容器去實現這個標準,採集對應的數據。但因爲容器沒有辦法定製,那對於前端頁面投放到段外的場景,這套標準要如何實現兼容?阿里提供了一套基於Performance Observer跟Mutation Observer的近似計算方案,每次dom變化時就收集對應信息,以上報數據的方式來實現Web端的兼容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"Hybrid API如何像Web API一樣實現兼容適配?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"首先統一了一套前端跟客戶端的調節協議——MethodChannel,在這個基礎之上,阿里拉通了集團內大概50多個的App,統一了一套Hybrid API標準(包含55個通用的API),同時提供了配套的數據監控、CanIUse等工具,保證大家在接入這一套JS Bridge的情況下,在不同端內都能夠實現平滑的切換。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"跨端展望:自繪、多中心化、大割裂"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"從Flutter開始我們就認爲自繪渲染可能會成爲未來所有跨端技術的標配,它重點解決的是雙端一致性問題,但是從發展的角度來看,實際上自繪能夠降低OS的限制"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。在Google的戰略Flutter是強綁了Future OS的,如果將OS比作一張畫布,自繪提供的能力就是在任何一張畫布上都能夠把上層的Web技術、Native技術、動態模板技術、Canvas技術完整的進行實現。其帶來的最大價值就是未來不管是否會出現新的OS或者業務場景,依然可以實現客戶端基礎能力的快速搬遷,不需要進行重複建設。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"其次,從業務視角來看,今天的互聯網正在變成多中心化"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"。在 PC時代,Web是真正的互聯互通,移動時代下流量被分在不同的App端內,逐漸演變成一個多中心化的時代。這會帶來兩個變化,一是目前已經有所顯現的流量堅定,二即每一箇中心化App都有能力去定義規範和規則。在2014、2015年的時候很難想象會出現類似小程序的方案,因爲它的流量結構體系不太可能讓人願意去花費巨大成本適配它的解決方案,在今天小程序卻已經司空見慣,而且所有開發者不得不去適配兼容不同的標準。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"那中心化會衍生出什麼問題呢?今天我們能夠通過Terror、UnitApp、Rax這些方案抹平不同的小程序之間的差異,實現一套代碼無縫裝載到多個App端內。但深思一下,如果這種發展趨勢,不同端的技術選型架構模式可能會出現越來越大的分歧和差異,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"當到了某一個臨界點時,我們會發現一次開發多端適配獲得的效率優勢完全沒有兼容性帶來的成本高,這將可能導致一個大割裂"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"——未來團隊的組織架構不再是前端開發工程師、iOS開發工程師、安卓開發工程師,而是微信小程序開發工程師、字節小程序開發工程師、阿里小程序開發工程師。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"所以說再看這兩個數據,今天  JavaScript、TypeScript等語言的開發者全球超過1000萬,這波開發者是全球最大的語言開發者,就是我們單從一門語言來說,JS是當之無愧的世界第一。其中有大概有500萬的前端開發者,這個數字在國內大概是50萬,這個是一個"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"基於此,很高興能夠看見2020年底小程序在W3C的規範組正式成立,這也表明還是有很多開發者願意爲了更好的標準化、更互聯的一個Web世界去做努力。也"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"希望每一個前端的從業者能夠更多地關注標準、思考標準、推進標準"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":",助力Web技術實現更好地互聯和互通,幫助Web技術贏下移動互聯網之後下一個設備平臺。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"嘉賓介紹:"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"張舒迪 ( 聖司 ),飛豬用戶前端及數字化經營團隊負責人,致力於移動前端、中後臺技術基建及在旅行場景的業務落地;阿里前端委員會跨端技術方向負責人,着力推進跨端容器器及研發框架標準化建設,完善配套設施。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"今年 5 月 29-31 日,QCon 全球軟件開發大會將在北京舉辦。大會匯聚上百位演講嘉賓,同時設立 30 餘個熱點技術專場包括 Serverless、Flutter、DDD、音視頻、雲原生、智能金融、大數據、數字化轉型、人工智能等,內容源於實踐並面向社區。【掃碼】或點擊【閱讀原文】瞭解更多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"目前會議限時 "},{"type":"text","marks":[{"type":"color","attrs":{"color":"#ff0000","name":"user"}},{"type":"strong"}],"text":"9"},{"type":"text","text":" 折,購票立減 "},{"type":"text","marks":[{"type":"color","attrs":{"color":"#ff0000","name":"user"}},{"type":"strong"}],"text":"880"},{"type":"text","text":" 元,團購還有優惠!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"☞ 有任何問題歡迎聯繫票務小姐姐 Doris:17310043226(電話同微信)"}]},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/1a\/8e\/1a62d94c56d983764b03d0175191198e.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章