上一篇关于Cordova的文章简单介绍了Cordova的主要实现代码和Cordova对web容器和插件相关的处理,本篇将着重介绍Cordova在平台化应用中的深度定制化。目前原生开发者持续不断的在受到前端开发者的冲击,前有Cordova相关的hybrid类APP,后有weex、RN等框架的出现,固然我以为weex、RN类技术是更应该被提倡的,由于他们兼顾了JS开发,原生渲染的特色。可是Cordova类框架也有它们的优势,微应用彻底可使用任何前端框架来编写,而且可控性强,虽然在渲染层面和weex、RN有必定的差距,可是目前咱们使用react.js这种单页面应用配合离线包策略,也必定程度上下降了它们在渲染层的差距,有效快速的解决发版周期之间的矛盾、跨平台开发、实时发布等一些问题,并且有效地保证了发布质量和线上问题的及时修复。css
1.基于Cordova交互插件化,打包提供给H5使用,H5只须要引入一个文件就可使用native提供的各类原生功能,包括但不限于fetch,map,IO操做,文件操做等。html
2.提供“离线包”策略,提高渲染速度和避免没必要要的网络请求,进行构建微应用。前端
3.提供三方受权服务,进行微应用的单点登陆受权功能。react
可见,经过运营商的网络上网,状况比较复杂,通过的节点太多;运营商的网络信号强度变化频繁,链接状态切换快;网络延迟高、丢包率高;网络创建链接的代价高,传输速度快慢不等(从2G到4G,相差很大)。程序员
压缩代码,尤为是js和css资源,压缩后的大小能够下降至原来的1/3如下,有效节约流量。web
200是一个正常的response,咱们在浏览器中打开一个网页(后面会讲如何针对移动端进行调试),还会看到304,即命中浏览器缓存。这两种状态是正常的http status。浏览器
30二、301跳转是常见的跳转,尤为前一种,在咱们进行鉴权的时候有时会用到,但这个作法要尽量地优化,一个页面访问,最多只进行一次302跳转便可,切忌频繁地跳转。缓存
40四、500,咱们对本身开发的代码比较注意,通常不会发生,可是有的时候,加载第三方库,尤为是第三方库中有本身load组件的操做,这时,404和500错误可能会在你不知不觉的时候发生。安全
业务的js和css可能常常会有更新,若是命中浏览器缓存,可能会让一些新的特性不能及时展示,甚至可能致使逻辑上的冲突。所以对于这些js、css的资源引入,最好用版本号或者更新时间来做为后缀,这样的话,后缀不变,命中缓存;后缀改变,浏览器自动更新最新的代码。bash
缓存接口数据,在一些数据新旧敏感性不高的场景下颇有做用,在非首次加载数据时候优先使用上次请求来的缓存数据,可让页面更加快速地渲染出来,而不用等待一个新的http请求结束以后再渲染。
钉钉的审批微应用,使用的就是单页架构。在这种架构下,基本不存在页面跳转的等待时间,只须要执行js逻辑触发界面变化,最多进行一次网络请求,得到服务端数据,其余资源均不须要再次请求。
再快的网络交互,毕竟也是跨越了数个网络节点,所以一张图片、一个js,优化到了极致,也照样可能须要几百毫秒的时间来得到。所以想要打破这个极限,就要使用资源离线的策略。网页代码里面加载网络资源的需求,就变成了直接加载本地文件,速度天然获得再一次巨大的提高。
有时,咱们可以经过用户的行为统计,预判出用户下一步可能进行的操做。假设,咱们统计出来针对某个微应用,用户首页渲染完成以后,大部分会点击列表中的第一个项目查看详情。那么在首页渲染完成以后,咱们就能够先预先加载第一个项目的部份内容,那么针对这部分用户,他们实际点击以后,当即就能看到新的页面中的内容。
起初咱们的H5代码都是随着应用一同发布的,这样不一样平台的应用天然会将相应的cordova.js打包到应用中,加载相应的cordova_plugin.js,初始化js插件。因为应用逐渐走向平台化发展,那么咱们的问题来了,怎样将咱们的插件给其余三方应用使用?让其余系统能够方便的集成和使用?基于这样的需求,咱们引入了两个自定义的js文件,cordova-dynamic-loading.js
和pm-cordova.js
。cordova-dynamic-loading.js须要在业务端代码前引入,用于检查cordova环境,而且根据客户端系统,初始化不一样的插件。而pm-cordova.js是js-bridge接口封装库。
架构图大体以下:
![]()
1.H5应用层:目前基于react.js构建的单页面应用,取消页面跳转带来的没必要要的网络加载。
2.插件API接口层:平台对外输出插件接口。
3.平台标识:其实是为浏览器设置的惟一标识,区分不一样平台,便于加载不一样的js插件使用。
4.动态加载:基于不一样平台的userAgent,动态加载相应平台的js插件,其中cordova-dynamic-loading.js和cordova.js为具体实现代码。cordova.js还承担着原生与js交互对插件id,参数的传递,回调的存储等相关工做。
5.平台:不一样平台提供相应平台的js插件,供cordova.js加载。
具体这两个文件的实现代码由前端大神编写的,这里就不粘了,做为一个菜鸟iOS程序员,前端目前还尚处于学习中。固然框架内还提供了自定义的pm-cordova-mock.js
可选引入,是pm-cordova.js的web端mock版本,本地开发业务进行测试时能够引入这个文件替换pm-cordova.js。除此以外,平台内也一样内置了一套相关代码,三方应用彻底能够没必要集成插件相关代码也能够与平台进行交互。
UIWebView没有提供设置UserAgent的接口,可是能够经过cordova框架内CDVUserAgentUtil
类,设置userAgent。
@interface CDVUserAgentUtil : NSObject
+ (NSString*)originalUserAgent;
+ (void)acquireLock:(void (^)(NSInteger lockToken))block;
+ (void)releaseLock:(NSInteger*)lockToken;
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken;
@end
复制代码
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken
{
//断言
NSAssert(gCurrentLockToken == lockToken, @"Got token %ld, expected %ld", (long)lockToken, (long)gCurrentLockToken);
//设置userAgent
//必须在实例化UIWebView以前设置UserAgent。
//它是按实例化读取的,因此它不会影响之前建立的视图。
//除外!当一个PDF被加载时,全部当前活跃的uiwebview从新加载它们
//来自NSUserDefaults的User-Agent在PDF bah的DidFinishLoad以后的一段时间!
NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:value, @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
}
复制代码
经过设置NSUserDefaults中UserAgent的值来修改,可是这种设置方法有一个限制,须要在UIWebView的loadRequest以前调用才能生效(加载PDF比较特殊)。这是Cordova源码中关于这个问题的描述。
- (NSString*)userAgent
{
if (_userAgent != nil) {
return _userAgent;
}
//省略部分源代码
_userAgent = [NSString stringWithFormat:@"%@ %@", _userAgent, @"[AWV/v1.0.0;AD/iOS]"];
return _userAgent;
}
复制代码
Cordova框架内提供对浏览器userAgent的设置相关代码,平台内对userAgent作了一些定制化的内容,好比自定义了userAgent参数,这里的userAgent实际上就是提供给上面cordova-dynamic-loading.js
文件进行解析使用的。实际上经过Cordova源码能够看到还有一些包括对userAgent的释放等其余操做,这里不作具体分析。关于CDVUserAgentUtil能够参考Cordova源码解析(二)- 自定义UserAgent。
1.对三方系统提供cordova-dynamic-loading.js
文件,用于检查cordova环境。
2.提供pm-cordova.js
文件用与对js桥封装。
3.提供pmCordova.platform.ready
(其实是对外提供的一个插件)函数,因为cordova插件异步进行加载和初始化,用于保证安全调用。
4.提供高级配置,三方系统可按照本身的需求对插件进行配置,减小须要加载的plugin,提升性能。
5.提供pm-cordova-mock.js文件,用于本地开发业务进行测试使用。
为何要提供离线包,前言也有说明,这里再详细说一下:
1.尽可能使容器内H5接近native体验,将HTML,JabaScript,CSS压缩为离线包动态下发。
2.减小网络环境对H5应用的影响。
3.提高打开H5的速度,减小白屏渲染时间。
4.实现微应用的动态更新。
经过应用管理平台,发布应用时会配置相应的版本号等信息,客户端启动后会后台比对版本等信息,检查应用是否须要更新等。关于离线包策略,为了防止用户阻塞,服务器会部署一套彻底同样的代码,在离线包没有下载好的状况下,客户端会先去加载服务端的代码,待下次进入该应用的时候加载本地离线包内的代码,这一块的处理和前几天看到支付宝移动端动态化方案实践有些类似。
离线包统一由打包平台一键打包发布,从代码获取到压缩,到生成zip包,再到发布一鼓作气,并相应的对版本号进行递增,目前平台客户端支持增量更新和全量更新,增量更新能够更节省流量,更新速度更快。全量更新能够保证版本更稳定,安全性更高。基于此,平台客户端实现了应用的动态发布与更新以及线上bug的修复,不须要再从新打包发布。
客户端经过不一样应用的应用id扩展不一样的应用安装路径,用于应用的安装和打开。 实际上在客户端最终会有这样的目录结构:
- (NSURL*)appUrl
{
//省略了一部分代码
NSURL* appURL = nil;
if ([self.startPage rangeOfString:@"://"].location != NSNotFound) {
} else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
} else if([self.wwwFolderName hasSuffix:@".bundle"]){
} else {
// CB-3005 strip parameters from start page to check if page exists in resources
NSURL* startURL = [NSURL URLWithString:self.startPage];
NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
}
return appURL;
}
复制代码
主要作的工做所有在NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
内实现,这行代码会生成咱们预设好的目录文件路径。 Cordova内部实际上默认加载的路径为bundle,须要修改成咱们指定的沙盒路径而后去加载沙盒里面相应的微应用进行UI展现。
应用有了,也提供了离线包策略,那么三方应用怎么登陆平台?基于这个需求咱们提供了Oauth2服务,进行三方登陆受权。这部分不在web容器范畴内,后续会具体分析。
注:以上工做的实现以及web容器优化方案来源于民生办公开发团队。