飞猪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"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章