|
「福利」✿✿ヽ(°▽°)ノ✿:文章最后有抽奖,转转纪念T恤,走过路过不要错过哦前言目前转转App是一个典型的HybridApp,采用的是业内主流的做法:客户端内有大量业务页面使用WebView加载H5页面承载。其优点是显而易见的,即:Web页面上线频度满足快速迭代的业务需求,不受客户端审核和发版的时间限制可以将各个业务线的开发工作分摊到各个业务的前端团队上,使得个业务线可以并行开发。其存在的问题也是很明显的,比如加载性能问题,白屏问题,界面展示的局限性、操作的局限性,无法使用系统功能等。转转HybridApp架构转转Hybrid架构设计如下图转转整个Hybrid容器架构设计分为四层原生Web页面这块就是大家常见的Web开发环境,可以通过Vue、React等实现。Web增强包括像离线包和Web页面容器等。中间件层中间件层使Web页面容器和转转底层框架有机结合起来,同时还提供各种生命周期机制、事件机制、扩展插件等内容。中间件层通过JSBridge将客户端以及中间件层提供的各种能力和Web前端代码进行联通。转转基础架构主要包含native相关的能力架构通信模块-JSBridgeJSBridge是WebView容器的基石,桥接了JS环境与Native,实现了Native代码和浏览器环境的双向通信。Native代码可以通过调用浏览器提供的接口运行JS,从而实现调用JS函数、传递参数到JS环境等。而浏览器到JS环境的通信是通过Native拦截浏览器的请求来实现。通过JSBridge我们就解决了WebView界面展示的局限性、操作的局限性和无法使用系统功能等问题。JSBridge原理如下图预加载模块-离线包系统为了做到良好的用户体验,我们在容器中引入了离线包机制。通过离线包机制,我们将原有从线上加载的Web页面,提前下发到本地,通过读取IO或者内存来进行页面的渲染,达到接近原生的用户体验。通过发布平台,我们可以将不同的Web页面离线包,以单独应用的形式,进行不同维度的下发,使原来Native发布模式,改为各业务线自行定制发布计划,自行制定发布标准,自行发布的并行发布形式,来满足业务的快速迭代。Web应用资源离线系统方案图下面我们从前端构建、测试、发布和APP端策略两个方向分别来分别阐述:前端构建、测试和发布我们采用腾讯Alloy团队出品的Webpack离线包插件:Ak-webpack-plugin,其可以根据配置,将Webpack的构建出的静态资源,压缩成映射了静态资源在CDN路径URL的ZIP压缩包。同时在配置的过程中,也可以选择排除掉部份文件(比如图片,并不是所有构建出的图片都是用得到的)。其生成的压缩结构如下:image在此过程中,我们不需要关注资源之间加载的依赖关系,更不需要关注具体的业务逻辑。只需要关注Webpack构建后生成的资源文件夹的结构。把生成和上传离线资源包的过程封装成在npmrunbuild中,通过脚手架生成项目可以让业务方无感接入。生成的离线资源如何上传呢?我们利用持续集成和发布工具Beetle(转转自研的CI系统,可以理解为Jenkins)来自动发布。在本团队的发布与上线流程中,Bettle代替Fe工程师构建与部署前端项目。使前端项目也像传统的App与后端项目一样做到了开发与构建部署分离,提高了团队的效率。而我们生成和部署离线包的操作,也交由Bettle替我们完成。当部署到测试环境时,Bettle会把离线包发布到测试版的离线包环境中,当正式线上时,还会发布到正式环境。这样整个测试流程也可以测试离线包。避免离线包造成的一些问题。APP端策略在客户端每次启动或重新打开一个Webview的时候,都会通过接口读取一份最新的各业务线的离线包与版本号的配置表。App根据本地的配置文件和线上的做对比,下载需要更新的离线包资源。资源包加载的优化增量的资源更新(bsdiff/bspatch)客户端内置(或者在WIFI环境下载)了各业务全量包的基础上,为了减少每次下载更新的资源包的体积,我们采用了增量更新策略。该策略具体为:每次发布版本的时候,如果此业务线之前已有离线包,则通过离线系统生成差分包放在CDN。增量更新的策略使用的是基于node的bsdiff/bspatch二进制差量算法工具npm包bsdp。客户端下载差分包后使用Bspatch合成更新包。单独控制各个业务线Web应用是否使用离线机制为了更好的监控离线包服务端和客户端的运行情况,并且降低使用离线资源带来的不可预料的风险,将隐患做到可控。我们在每一个业务上都加入了使用离线资源的开关和灰度放量的控制。数据一致性校验与数据安全性为了防止客户端下载离线资源时数据在传输过程中出现窜扰,导致下载的离线包无法解压,我们在服务端通过接口中将资源包的md5值告知客户端,客户端下载后通过计算得到的资源包的md5值,与之比较,可以保证数据的一致性。同时为了保证传输过程中,资源文件不被篡改,我们将上述的md5值通过RSA加密算法进行加密。在服务端和客户端分别使用一对非对称的密钥进行加解密。离线包批量下载在启动App时,App会集中批量的下载各个业务线的离线包资源,我们在存放离线包资源的CDN中使用了HTTP/2协议,这样客户端与CDN只需要建立一次连接,就可以并行下载所有的资源。在需要下载离线包个数较多的情况下,可以比传统的HTTP1有更快的传输速度。同时,客户端只需要运行一次下载器。减少多次运行下载时对手机CPU的消耗。基础包包自动更新当增量包的体积达到基本包的60%以上。我们就认为需要更换基础包。离线包回退机制在实际情况中,为了避免用户下载离线资源或者解压资源失败等问题,导致无法加载相应的离线资源,我们设计了回退机制,在本地内置的Base包(ZIP文件)解压失败的时候离线系统接口超时下载离线资源失败增量的离线资源合并失败等情况下遇到上述情况时,我们会转而请求线上文件。WKWebView离线包实现iOS系统存在两种Webview类型分别是WKWebView和UIWebView,UIWebView自iOS2.0就有,而WKWebView从iOS8.0才有,毫无疑问WKWebView将逐步取代笨重的UIWebView。UIWebView有很多问题,比如占用过多内存等。WKWebView提升是在多方面的,比如网页加载速度有很大的提升,内存提升也非常明显,支持更多的HTML5的特性等。转转在很早的时候就切换到WKWebView。但是WKWebView不能像安卓一样,直接拦截请求。WKWebView中实现离线包一共有三种方案,分别是私有API方案自定义协议方案LocalWebServer方案转转在实践中都做了尝试,其中遇到了很多问题,这里就不单独展开了,大家可以关注我们,期待我们的下一篇文章《WKWebView离线包实践》。离线资源管理平台对于接入了离线系统的各个业务线的前端工程生成的不同时间版本的离线包,我们需要有一个直观明晰的方案来对其进行管理。于是,我们开发了离线资源管理平台,对接离线后台系统,系统界面如下图其主要的功能包含有:查看与管理各个业务线信息及其离线功能的灰度放量的比例。对于新接入离线系统的前端工程,灰度放量可以使得部分用户先使用其离线的特性,并防止不可预料的问题发生在全体用户上。通过业务线,版本号,发布时间等条件,查询各个版本的离线资源包的列表及其详细信息。如离线包的类型,体积,上线时间等属性。并在此基础上允许将某版本的离线包下线,以实现离线资源版本的回滚功能。针对全局的离线功能,提供了离线功能的开关。公共资源预加载在离线包系统中,因为我们是按业务线划分,所以并没有公共资源包。因此,我们设计了一个利用浏览器缓存策略来缓存公共静态资源的策略。App端为提高WebView打开的效率,会使用WebView复用池策略。其策略的第一步就是在App启动时会初始化一个备用的WebView。我们正是利用了初始化的WebView,来加载一些公共的资源,这样可以让之后的页面再加载该资源时,可以使用HTTP缓存。我们开发了一个管理平台,用来实时的管理App需要加载的公共资源文件。页面接口预请求我们通过离线包解决了资源加载的问题,但是首页的接口请求也是一个需要优化的地方。现有Web页面加载流程可以简单概括如下:先是加载JS,在生命周期中请求数据,等待数据返回,渲染页面。那我们是否可以通过客户端提前帮忙请求接口,然后直接从客户端把数据取回来?答案是肯定。PS:接口预请求的方案,适合首页请求了多个接口的项目,如果只有一两个接口,效果没有明显提升。如何告诉客户端我们需要请求这些接口我们通过离线包的配置文件上新建一个ajax字段:ajax: { "mode": "hash", "split": "#", "routes": [ { "router": "/content/index", "requestOffline": true, "route":[ { url: '/zzgift/creditandexchangecount', key: 'creditandexchangecount', des: '获取星星信息', method: 'post', isNeedLogin: false, params: {} } ] } ]}首先,用户点击打开Webview,App请求Ajax列表的接口并且把本地的上次请求的数据删除,同时加载WebView。JS在请求接口时,先通过JSBridge取数据,Key支持多个值一起传。如果数据在JSBridge请求之前返回,把数据写入本地对应Key的Value,JSBridge读取后返回数据。如何没有返回,读取本地Key为空,直接返回空。接口返回的数据App端不用做任何判断和处理。如果没有取到,页面再次通过JS发起请求,之后的超时重新请求等策略保持一致。图片骨架屏Hybrid界面体验问题有一个很大的问题就是页面白屏转转通过图片骨架屏来解决页面白屏的问题,在WebView初始化时,客户端把一张设计师出的的图片,覆盖在WebView上面。当页面WebView加载完成,或者前端通知客户端加载完成。客户端通过渐隐动画来隐藏图片,达到完美的过渡效果。读取配置文件客户端在启动时读取图片骨架屏的配置文件:// 传入设备分辨率ratioWidth:400, ratioHeight:500{ "code":0, //code是0 代表请求成功 -1代表图片骨架屏功能关闭 "data":{ "m.zhuanzhuan.com/enjoy-given/eg/index.html": { "rege": '#/content/index' "routes":{ "#/content/index": { "downloadUrl": 'https://m.zhuanzhuan.com/pic.png?400*500' "imgName": 'pic.png', "id": '10001', } } }, "msg":"" }}客户端在App启动的时候,读取接口。如果是首次使用,遍历配置文件,建立路径,下载图片。如果是二次使用,则对比两次的配置文件,如果图片名称不同,删除id文件夹下面的图片,下载新的,如果相等,保持不变。客户端在内存中建立文件zzSkeleton/10001/pic.png解析url并展示当客户端打开webview时,读取url例如:https://m.zhuanzhuan.com/enjoy-given/eg/index.html?__isonshowpro=1&metric=rhtjEzu4TBGu6npSjDyXcQ282171v&fromGiftPage=cateList&fromCateId=0#/content/index?ada=asdad取出m.zhuanzhuan.com/enjoy-given/eg/index.html(location.host+location.pathname在接口返回的配置文件中取出{ "rege": '#/content/index' "routes":{ "#/content/index": { "downloadUrl": 'https://m.zhuanzhuan.com/pic.png?400*500' "imgName": 'pic.png', "id": '10001', } }}根据rege解析出#/content/index路由,然后根据id和imgName来从本地取出文件然后根据zzSkeleton+10001+pic.png读取本地的图片,并展示在WebView上如何解决图片拉伸的问题后台配置图片的时候,可以根据不同的分辨率来配置不同的图片,类似App配置启动图的逻辑。在客户端请求配置文件时,会传入设备分辨率,接口会根据分辨率返回最接近的图片。App统一跳转平台对于App中的每一个页面、或者是每一个行为,我们都能使用一个URI来定义,这个URI在转转内部叫做统跳。转转的统跳全部由统跳平台来管理,通过统跳平台可以更好的测试、分享和管理这些统跳地址。image系统比较简单,通过动态表单来添加参数。一键生成二维码来快速的测试。Hybrid总结与展望目前转转Hybrid整体的技术方案,包括离线包、图片骨架屏等,已经无痛的接入业务中使用,很好的解决了页面加载性能问题,白屏问题,界面展示的局限性、操作的局限性,无法使用系统功能等问题。当然技术在不断的发展。转转Hybrid建设在追求技术进步的道路上。还有很多的地方需要提升。提供完整的SPA体验目前页面跳转基本是重新打开一个WebView,这样就损失了一些项目的性能。我们计划通过前端接管原生的路由的方式,让端内做到真正的SPA体验。接口请求和埋点通过客户端发送转转现在的请求和埋点都是通过前端自己发送的。但是存在埋点丢失等问题。我们计划打通客户端和前端的请求发送。同时前端也可以利用客户端的安全鉴权等能力。本月文章预告预告下,接下来我们会陆续发布转转在微前端、Umi、组件库 等基础架构和中台技术相关的实践与思考,欢迎大家关注,期望与大家多多交流文末福利转发本文并留下评论,我们将抽取第10名留言者(依据公众号后台排序),送出转转纪念T恤一件,大家转发起来吧~
|
|