背景
开发中 WebView是很常用的技术方案 相比native 也有着很明显的一些优点 如:
1、实现安卓 iOS的复用
2、不用发版 动态更新页面
3、节约native开发资源
......
但同时 也存在一些缺点 比较明显的就是 加载速度比较慢 用户体验不如native
所以 提高WebView的加载速度 提升用户体验是避不开的问题
WebView加载速度优化方案
一、WebView加载过程
想要优化WebView加载速度 首先需要了解下网页加载过程:
初始化webview -> 建立连接 -> 请求页面 -> 下载数据 -> 解析HTML -> 请求 js/css 资源 -> dom 渲染 -> 解析 JS 执行 -> JS 请求数据 -> 解析渲染 -> 下载渲染图片
二、WebView优化方案
1、提前初始化WebView
当App打开时,默认是并不初始化浏览器内核的;只有当创建WebView实例的时候,才会创建WebView的基础框架。
所以与浏览器不同,App中打开WebView的第一步并不是建立连接,而是启动浏览器内核。
提前初始化WebView可以节省这部分时间
提前初始化WebView分为两种情况
① 刚启动 还没打开过WebView
可以初始化一个全局单例WebView 在APP刚启动时就初始化 打开具体页面是都复用这同一个WebView
这种方式存在的一个缺点就是如果用户不打开WebView 会造成资源的浪费
② 每次打开WebView时 如果之前没有打开过 把当前WebView存到内存中 下次打开 直接取出来使用
③ 对于确定性比较高的常用网页 可以在启动时直接保存到内存 用户打开时就可以直接显示
2、预存HTML到本地
APP启动时预存HTML到本地 打开WebView准备加载HTML文件时 WKURLSchemeHandler 拦截请求资源判断资源是否和本地资源一致(一致则返回本地资源文件,不一致则请求网络资源)
这种预存方式不能处理Http、Https等常规scheme 本地资源不存在时 请求网络资源时 可能需要主动转换成http/https
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask {
self.holdUrlSchemeTasks[urlSchemeTask.description] = @(YES);
/// 优先加载本地资源,本地没有加载网络资源化
NSString *urlString = urlSchemeTask.request.URL.absoluteString;
NSString *fileName = [urlString lastPathComponent];
NSString *markStrig = [NSString stringWithFormat:@"%@/",NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject];
NSRange range = [urlString rangeOfString:markStrig];
if (range.location != NSNotFound) {
fileName = [urlString substringFromIndex:range.location];
}
NSString *mainBundlePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *htmlPath = [mainBundlePath stringByAppendingFormat:@"/"];
NSString *filePath = [htmlPath stringByAppendingFormat:@"%@",fileName];
NSFileManager *fileManager = [NSFileManager defaultManager];
/// 判断文件是否存在
if ([fileManager fileExistsAtPath:filePath]) {
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:urlSchemeTask.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil];
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
} else {
NSString *schemeUrl = urlSchemeTask.request.URL.absoluteString;
if ([schemeUrl hasPrefix:@"qekj"]) {
schemeUrl = [schemeUrl stringByReplacingOccurrencesOfString:@"qekj" withString:@"http"];
}
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:schemeUrl]];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *number = self.holdUrlSchemeTasks[urlSchemeTask.description];
BOOL flag = number.boolValue;
if (flag == NO) {
return;
}
if (response) {
[urlSchemeTask didReceiveResponse:response];
} else {
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:urlSchemeTask.request.URL MIMEType:@"未知类型" expectedContentLength:data.length textEncodingName:nil];
[urlSchemeTask didReceiveResponse:response];
}
[urlSchemeTask didReceiveData:data];
if (error) {
[urlSchemeTask didFailWithError:error];
} else {
[urlSchemeTask didFinish];
}
});
}];
[dataTask resume];
}
}
3、H5的css、js文件、图片资源压缩处理
这个需要H5同学帮助
H5的css、js文件压缩方案
4、js、css、image等资源进行离线缓存
WKWebView是有缓存策略模式的 通常情况下 我们是关闭缓存的 不然可能会出现数据不能及时更新的问题
如果开启了本地缓存配置 建议H5链接增加一个类似于版本号的标识 如果内容有更新 则版本号+1 WKWebView的缓存是通过urlString来判断是否需要重新加载 版本号变化 会认为是新的网页 则会重新加载
参考
支付宝移动端动态化方案实践
Web离线技术
WKURLSchemeHandler协议优化HTML加载速度
iOS WebView的性能、体验优化
移动 H5 首屏秒开优化方案探讨
iOS html5使用缓存并及时更新方案总结