飛豬Flutter技術演進及業務改造實踐

{"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":"隨着 Flutter 生態逐漸完善,阿里集團在移動領域已經大刀闊斧地開始了新方向的基建和研發模式的改革。飛豬今年擔任着集團技術委員會技術創新的一個重要任務,探索大前端模式下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":"link","attrs":{"href":"https:\/\/www.infoq.cn\/video\/6Ftkjr2A006v8iew6ifl","title":null,"type":null},"content":[{"type":"text","text":"InfoQ技術公開課直播"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"欄目所做的分享,結合飛豬近半年來在Flutter技術實踐中的突破和探索,重點介紹跨端標準容器建設、組件庫的沉澱、性能優化的經驗,以及面對存量業務做Flutter改造的新思路。"}]},{"type":"image","attrs":{"src":"https:\/\/uploader.shimo.im\/f\/Az98Coxdppdm5obw.png!thumbnail","alt":null,"title":null,"style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":false,"pastePass":false}},{"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\/fe\/fe82fb0aaa8de6df8756df1b0b3e30bc.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":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"架構1.0:"}]},{"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":"2013年,隨着阿里巴巴All in Mobile,阿里旅行也有了獨立的App。最初業務比較簡單,PC頁面簡單改造成H5運行在App端,以信息展示爲主,Native業務承接機票交易,整個App的構建發佈不成體系。發渠道包都在單臺Jenkis服務器上現拉源碼編譯,測試驗證後直接發應用市場,沒有體系化的研發構建平臺,也缺乏完善的灰度機制。"}]},{"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":"當時Android手機山寨機橫行,主流廠商各種定製ROM,兼容性、適配問題搞的焦頭爛額,所以我們主要精力在解決穩定性問題,同時夯實基礎功能,支撐業務穩定上線。這個階段Android、iOS得到獨立發展,只在頁面跳轉協議和事件總線協議上保持一致。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"架構2.0:"}]},{"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":"我們在端研發框架上對接了成熟的支付寶Mpass\/手淘Atlas框架,也接入了夥伴\/MTL研發協作平臺,有力保障了多人並行開發,業務迭代最快做到按周交付。同時基本建立了完善的灰度發佈體系,Crash率從千分之5降低到萬分之2,其他基礎功能(網路庫\/圖片庫\/Push通道)也逐步從自建方案迴歸到依賴中臺基礎能力,研發效率大幅提升。"}]},{"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":"但受限於應用渠道更新效率,在Android端,新版本升級覆蓋慢的問題長期存在,分發效率低的問題不得不提上日程。因此我們開始投入做相關技術的改造:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"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"}}],"text":"看Native自身,各種動態化技術層出不窮,基本上頭部的廠商都有自己的方案,基本的思路都大同小異。飛豬也積極參與了支付寶的動態更新技術共建,後面手淘也開源了Atlas框架,很多公司還在使用。從我自身體感看,因爲是hook技術,對系統的穩定性挑戰還是比較大的,在非航母級應用上,動態發佈能力主要還是修bug,真正推業務的情況並不多見。隨着新版Android系統加強對隱藏方法的限制,再加上hook框架享受不到系統層的一些性能優化,這條路基本走到頭了。爲了進一步提升業務對Native動態性需求,飛豬也與手淘共建了DX動態模板容器,通過統一動態模板DSL,建立了完善的搭建平臺和模板管理倉庫,現已廣泛應用於業務中,但只限於簡單業務邏輯。"}]}]},{"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"}}],"text":"看前端,H5因爲開發效率高,且具備完備的動態化能力,開始大量承接業務。我們也成立了獨角獸跨棧小組,通過離線包、預加載、預渲染技術來優化H5性能,後面在Android端內置U4 WebView\/iOS升級到WKWebView,整體的性能和兼容性有了更大的提升。不滿足於現狀的技術團隊,通過把Web與原生渲染能力結合(Weex\/Rn),實現了前端性能接近原生的體驗。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"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":"隨着業務需求迭代,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":"技術側複雜度增加也容易出現紕漏,一次H5離線包在Android端由於沒加好保護導致線上大面積客訴,經過超過24小時排查才最終定位到問題。很多基礎線的改造也很難評估具體影響面,給穩定性帶來新的挑戰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/13\/1342b1bc139e6a11e2ba4c55ce53c9ec.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":"具體到業務開發遇到的挑戰,和端技術相關的包括UI渲染問題,系統兼容性問題,還有些動態性、研發效率問題從目前的技術方案上看,因爲兩端的具體實現依賴原生能力,系統平臺層的差異性帶來的業務表現差異問題,很大程度是需要更底層的技術方案來解決的。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"自渲染和Flutter"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/78\/78547dcca9971af937be0b40c6c65e40.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":"自渲染技術並不是新概念,從很早以前的QT到現在的Flutter,它的優勢在於具備非常好的跨端能力,能解決長尾問題。對比原生渲染方案,具體到WebView,在Android上版本碎片化問題非常多,給前端適配帶來很大的難度;看Native原生業務,之前提到的由於溝通上理解上偏差出現交付上差異,最終由測試兜底也是不可持續的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/92\/921f7ddf791522a1c888d4176ab4ce0a.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在全球超過200萬開發者,月活開發者50萬。據GitHub統計,Flutter是全球第二快增長的開源項目。Google Play 發佈了超過11萬Flutter應用,國內大廠基本都使用Flutter,阿里巴巴多個BU也積極參與生態建設。對比QT,同時自渲染技術,它在開發體驗上和生態建設上都有很大優勢,也因此收穫了很多開發者。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/8a\/8a4d95018d30481690555ca1d9530cc4.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除了因爲政策原因捨棄了動態性,其他方面表現都非常優秀。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"飛豬客戶端架構3.0"}]},{"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}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d9\/d9d10f1c7d4b1a5f2f4f14acd1154ddd.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":"2019年,飛豬開始在商家版EBK改造項目上試水Flutter,在研發效率和用戶體驗上都有不錯的收益。隨着Flutter混合技術棧的完善,我們決定2020年6月份在飛豬C端做Flutter專項改造,寄希望於推動飛豬技術架構升級,完成客戶端技術體系向大前端技術體系演進,建立統一移動端研發模式,摸高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技術,上層業務開發可以無縫對接Android、iOS,中間的Flutter容器非常薄,平臺差異性對接工作主要通過Flutter SDK實現,解決Native體驗的一致性問題,提升可維護性。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"阿里Flutter建設的現狀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/3b\/3b50c954dea0e548c7e9074302f87c7d.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是UI Tool Kit,最初定位是服務全新App開發,在實際中很多功能滿足不了存量業務開發需求。阿里Flutter基礎建設主要圍繞完善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":"目前阿里已有10+BU積極參與到Flutter業務開發,核心幾個BU也是生態的積極貢獻者,與Flutter團隊保持密切的鏈接,因此Flutter官方也在積極完善對混合棧的支持,期望未來Flutter在這方面有更優異的表現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f7\/f7cbda3cbd7f41f3ed3d87655700a76c.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":"2020年伊始,飛豬Android端在機票航變和酒店相關簡單業務做了Flutter試水,我們對接的首個版本是1.12,穩定性和性能都符合預期。對於這種簡單的純展示頁面,是非常適合用Flutter的。像機票航班詳情頁最近的新需求完全靠1人開發就可以。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/41\/4180fcfce5a0cc178b9ee1f2a328d86d.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信任感,後面開始在比較複雜的業務上試點,用到了非常多的基礎功能,包括視頻播放功能對接,爲後面全面開始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":"從性能表現上看,低端機性能上加載耗時優於iOS,幀率接近原生,CPU負載、內存消耗接近原生,整體的穩定性非常不錯。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/41\/41badc0ab28153bd3e1d345335241454.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業務試點同時,我們也在做Web on Flutter方面的技術攻關,目前在飛豬9.6.9版本上線了3個試點業務,性能表現優秀,H5代碼基本不用改造就能運行在Flutter上,後面在專項上會具體介紹技術細節。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"飛豬Flutter技術沉澱"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/6a\/6a3cefbe82c73c96bac0a0b8b4d4aa58.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實踐經驗。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Web on Flutter"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"背景介紹"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/84\/84c66a3192cf1db8fad0631102aae1db.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},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"我們通過Android端內置UC WebView,基本解決了WebView碎片化問題,適配壓力驟減。但在2020年雙11會場頁面開發中,還是存在一些UI展示低級缺陷問題,主要表現在字體顯示上,這些問題雖然不嚴重,但比較影響用戶體驗,拉低我們對品質的追求。另一方面,Weex 1.0依賴原生組件,兩端渲染一致性問題非常難解決,實現原理類似的RN也遇到類似的挑戰。"}]},{"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技術熱度興起,前端同學在探索Web on Flutter的可能,對比之前Weex 是Web on Native。底層渲染技術用Flutter替換後,會帶來性能上新的機會,一致性的表現將能更完美地解決。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/35\/35ec5d3fe370b5fb237ee024fc5823ca.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":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"技術選型與落地"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/a7\/a7b7105779579093d8e9c2366a6616e6.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":"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"}}],"text":"方案①:JS 封裝與Widget API 接近的接口,實現Flutter動態能力,該方案脫離了前端生態;"}]}]},{"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"}}],"text":"方案②:Web與Widget對接,ROI高,完成映射後性能基本接近Flutter原生,性能方面也有很高的想象空間;"}]}]},{"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"}}],"text":"方案③:Web與RenderObject對接,需要自己寫佈局相關邏輯,性能上沒想象的高,擴展視頻、地圖組件有一定改造成本,Flutter版本升級有一定成本;"}]}]},{"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"}}],"text":"方案④:用C++重寫Framework,拋棄Dart Rumtime,開發成本非常高。"}]}]}]},{"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\/bc\/bc8f20e9344ca0c51009cfb5152b67f1.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/65\/65f76c7871feea2763740463bcba1706.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":"Flugy由飛豬自研,支持阿里巴巴跨端標準子集,目前在技術層面也和其他團隊共建中。"}]},{"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是一個Widget Plugin,可以和Flutter生態打通,主要代碼用Dart開發,對接的是Flutter Widget(不需要定製引擎),從前端視角看就是個精簡的WebView, H5不用改造就能運行起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c2\/c28ec11ed7f038b69164cab63c5793b5.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":"渲染指令說明:對應的是DOM API的一些操作指令,包括 createElement,AppendChildren,removeChildren,replaceChildren,insertBefore..."}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Flugy標準容器關鍵突破口"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/67\/67490f7e2dae304e35e263398847d5aa.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和Flutter,雖然Flutter核心開發來自Chrome維護者,但從設計理念上更接近原生。前端開發只需要通過屬性賦值設置想要的結果,不用具體到要用什麼組件來實現,WebView把這部分邏輯都消化掉,而原生開發需要關注更多的細節,組件的封裝和組織關係,這也是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":"回到標準容器項目上看,需要Flugy容器把WebView之前處理的頁面佈局、樣式的邏輯實現好。與WebView不同之處在於我們對接的是W3C的子集,沒有歷史包袱。這裏列舉了幾個屬性映射有代表性難題。比如 Overflow:display 和 Position。"}]},{"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":"下面詳細描述下關於Position屬性的實現,通常原生開發是從整個頁面大的結構入手,然後具體到各子模塊的拆分。在Web on Flutter上,JS是在運行過程中創建DOM,是以流的方式發送渲染指令到容器側,是個實時解析的過程,我們在解析過程中構建Widget對象。但前端的定位屬性非常靈活會有脫離常規流情況,這就需要反覆修改之前構建的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":"我們對元素狀態做了一層抽象把問題簡化,首先在解析添加元素A時,遇到position爲sticky\/fixed\/absolute元素時,在創建元素A的時候會同時創建一個它的副本A'(副本會被標記爲逃逸元素,即escape爲true),隨後A元素原身將會留在其父容器中佔位(不做渲染展示),繼續走正常的佈局流,以確保其它同級元素位置的正確性。而它的副本A'將會通過內部的通信機制向上尋找能夠接納它的更高層級的父級容器,然後在目標父級容器中按照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":"得益於Flutter有狀態Widget的設計和Flugy內部有效的通信機制,每一步操作都被控制到了一個最小範圍內,僅進行局部更新,而不會觸發整顆樹的更新,使得性能得到保證。其他很多屬性在Flutter都有具體實現,實現的複雜度基本可控。對齊標準是個比較細緻的工作,也容易出現理解不到位導致實現效果不符合標準要求,前端同學正在牽頭做標準容器單測事項,通過XRay實現各標準容器與H5渲染表現對齊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/6d\/6db1427b5d7d4e682df696452168d4d4.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":"上圖是在實現Position屬性繪製的元素層次結構關係圖。"}]},{"type":"heading","attrs":{"align":null,"level":4},"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}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/b9\/b911222590c029e1ce0c7a5f45ceeebf.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/18\/18d0038a8e3412cce68f8944b26833db.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Flutter優化實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/19\/19cb770666ae67f7354b057f0c811deb.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":4},"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"}},{"type":"strong"}],"text":"1. 圓角對性能影響"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d6\/d610a075164a8593b766b80ff6636004.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},"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":"2. 減少重繪"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/08\/087843380a5aa44a8721b58613c14ac4.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":"3. 開啓SurfaceView"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ae\/aefa0b61f2d8043a73c0f796baaa0d91.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/4d\/4de1d30eb40705e804032491f7fb5c5a.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/03\/031c36c68f5fbf2b9f683c834056a159.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":"image","attrs":{"src":"https:\/\/uploader.shimo.im\/f\/k3sJuAgA2Edw2I3b.png!thumbnail","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"組件庫建設"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/bd\/bd0cac2e6897611e27257684c0bde198.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},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"我們自己計算 "},{"type":"text","marks":[{"type":"italic"},{"type":"color","attrs":{"color":"#494949","name":"user"}},{"type":"strong"}],"text":"1dp"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":" 到底應該佔設備屏幕上的多少像素,而不是使用系統預設好的值。將計算好的這個比例設置到項目的環境中,這樣就可以以很低的成本達到按照實際分辨率適配出同樣的視覺效果,在開發時也會免去很多換算單位的麻煩。最近我們還支持Flutter字體大小不受系統設置影響的方案。UI 一致性完美支持後,UI自動化測試也能從中受益,只需要維護一套測試代碼,就可以在多設備下運行,降低適配成本。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/de\/de3229b15b47fcdc903a2e9dfe2dfbe1.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":"我們開源了11個UI組件,都是比較貼合業務場景的組件,上面列舉了幾個在社區受歡迎的組件。最近團隊主要精力在開發Flugy,後面會加強組件庫維護更新工作。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/43\/4317b84c9508ad4a6290ccd40618bf1c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"XRay自動化測試框架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/1b\/1bdb0f0b7c1c9f6841ca24bb3aa18dc6.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":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":"我們從手動測試操作過程找到靈感,基於圖像處理算法定位元素,通過驅動能力操控元素,然後再通過圖像處理算法驗證結果。整個鏈路是零侵入的,所以具備非常強的適應性。目前能非常好地支持Native、WebView、Flutter、小程序、動態模板等技術,並且也可以支持鴻蒙,以及FusionOS。"}]},{"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":"爲了提升測試代碼開發效率,我們基於IntelliJ開發了插件,只要熟悉Python就能熟練掌握,2020年爲了方便外包寫case,還提供了錄製回放功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/58\/5822e7479dca78911b679e824539c952.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":"這是整個測試case編輯、case執行情況的流程圖,最核心的部分是元素定位,用到了我們自研的圖像結構相識度識別算法,以及阿里達摩院的讀光OCR能力,在元素驅動技術上Android使用adb,iOS使用自研的方案Dwarf。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/60\/60c9c74e11bc5bc5aaa90137c06fe5dd.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":"列舉酒店列表和首頁測試執行情況,通過我們自研的圖像算法可識別出列表,測試代碼編輯只需要recognize_list,然後操作具體的item就可以。實際在XRay執行過程看是基於酒店列表生成了個類似Dom Tree的結構,在運行過程中操作的不是簡單的座標映射,而是對應到具體的元素模塊中,提升了可維護性和執行成功率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/01\/0148f59de39472bad19bdff93d007675.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":"除了XRay框架的建設,在實際業務使用場景上還涉及到設備調度,我們基於手機牆調度系統做簡單改造支持自動化測試設備調度,支持按系統、品牌、機型維度調配。廣泛應用於持續交付的驗證,爲無線質量保駕護航,目前也在和無線測試團隊做智能monkey。"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/3a\/3a43cbb17eb5f1ff97034d4695d68eb9.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/5f\/5ffe0a59a93ce2729d9bd65cb1d9e347.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/78\/783dd2bb7794e7415dc1d5ca469828d8.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":"link","attrs":{"href":"https:\/\/yuque.antfin-inc.com\/multix\/standard\/gdg5t6","title":null,"type":null},"content":[{"type":"text","marks":[{"type":"underline"}],"text":"Can I Use"}],"marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#494949","name":"user"}}],"text":":爲了能更好地支持容器同學和前端同學的研發,便於一目瞭然地瞭解各標準在容器層落地支持的情況,前端提供了CanIUse檢索平臺,查看標準在各容器側的支持情況,通過在真機上自動運行單測腳本做對比測試,確定容器的支持情況。"}]},{"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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c9\/c937a22e3be94636642b7c50379f3429.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":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/bb\/bbe8c1e3f878fc27967d36759ae48b65.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":"strong"}],"text":"直播視頻回放:"},{"type":"text","text":"https:\/\/www.infoq.cn\/video\/6Ftkjr2A006v8iew6ifl"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章