WKWebView 加载速度优化

因为H5的存在可以快速发布,不受审核影响的特点。目前存在很多公司,对于一些尝试性的功能,会优先采用H5来实现。甚至有些公司为了节省用人成本,直接在原生APP的壳子里完全嵌入H5页面。但是H5的体验比起原生来还是差了挺多的,那么怎么优化就成为一个需要解决的问题了。

首先,要先研究一下为啥H5的体检比较差,只有知道问题所在,才能更好的解决问题。H5页面给人直观上的感觉的就是加载的速度太慢,常常存在一段时间的空白情况。

那么为啥会存在这样的情况的,我们可以了解一下,H5的加载过程干了些啥东西。

初始化 webview -> 请求页面 -> 下载数据 -> 解析HTML -> 请求 js/css 资源 -> dom 渲染 -> 解析 JS 执行 -> JS 请求数据 -> 解析渲染 -> 下载渲染图片

哇塞,好大一堆事情,辛苦了。那么那些方面我们可以进行优化呢,这里就得看看为了解决这个问题而掉光头发的老前辈们的想法了。

  • 降低请求量:合并资源,减少 HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。
  • 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
  • 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存 localStorage。
  • 渲染:JS/CSS优化,加载顺序,服务端渲染模板直出。

方法很多,本文就介绍的方法就是要怎么减少网络请求从而提升H5的加载速度。

存在原生开发经验的同志都知道 WebView 加载H5的方式存在两种,一种是加载HTMLString,而另外一种就是加载url地址喽。本文就从这两方面来解读。

加载HTMLString

先奉上代码再做解释。

let htmlContent = "<p>我们都知道有些食物能治病,但如果吃不对,它们也能致病。有些食物,对于我们一般人来说,有着滋补的功效,但是对于高血压患者,它们最好别碰!<span >高血压的“三大杀手”</span></p><h1>1、韭 菜</h1><p>韭菜也具有发散性,有温肾助阳的功效。而大多数高血压患者属于阴虚体质,燥热、阳火过盛,多吃韭菜不但不利于血压控制,反而会加重病情。</p><p>2、猪 肝</span></p><p>猪肝中胆固醇含量很高,患有高血压、冠心病、肥胖症及血脂高的人忌食猪肝。</p><p><span >3、鸡 汤</span></p><p>鸡汤的营养价值很高,而多喝鸡汤又会使胆固醇和血压增高。因此,鸡汤不能盲目地作为病人的营养品,特别是患有高血压的人,不宜喝鸡汤。否则只会进一步加重病情,对身体有害无益。</p><p><br></p><blockquote> <span >除了上面三种美食,下面这些东西高血压患者也要少碰。</span></blockquote><p><span >1、味 精</span></p><p>研究表明,味精的主要成分是谷氨酸钠,如果平时钠的含量已经达到阈值,再多吃味精,显然会增高钠的摄入量,从而不利于高血压的控制。</p><p><span >2、“隐 形 盐”</span></p><p>酱油、黄酱、腐乳等调味脂高的人忌食猪肝。</p><p><span >3、鸡 汤</span></p><p>鸡汤的营养价值很高,而多喝鸡汤又会使胆固醇和血压增高。因此,鸡汤不能盲目地作为病人的营养品,特别是患有高血压的人,不宜喝鸡汤。否则只会进一步加重病情,对身体有害无益。</p><p><br></p><blockquote> <span >除了上面三种美食,下面这些东西高血压患者也要少碰。</span></blockquote><p><span >1、味 精</span></p><p>研究表明,味精的主要成分是谷氨酸钠,如果平时钠的含量已经达到阈值,再多吃味精,显然会增高钠的摄入量,从而不利于高血压的控制。</p><p><span >2、“隐 形 盐”</span></p><p>酱油、黄酱、腐乳等调味\\345\\223品含盐量高;腊肉、奶酪、火腿、榨菜也都含盐;话梅、薯片、椒盐花生等零食均是含盐食物。买食品时看下食品包装上的营养成分表,“NRV%”超过30%就要少买少吃。</p><p><br></p><p><span >3、浓 茶</span></p><p>高血压病患者忌饮浓茶,尤其是忌饮浓烈红茶。因为红茶中所含的茶碱最高,可以引起大脑兴奋、不安、失眠、心悸等不适,从而使血压上升。</p><p><span >4、饮 酒</span></p><p>研究表明,饮酒在一般情况下会让血压升高4到8毫米汞柱。大量、长期饮酒,更易诱发动脉硬化,加重高血压。因此,高血压患者应戒酒。</p><p>5、吸 烟</span></p><p>香烟中的尼古丁,能刺激心脏和血管,使血压升高,加速动脉粥样硬化的形成。高血压被称为“安静的杀手”,患病后可能没有症状,而大多数患者可能只有到体检的那天才知道自己得了高血压。</p><blockquote> <span >帮你“稳住”血压</span></blockquote><p><span >1、定期监测血压</span></p><p>家庭自测血压正常值应小于135/85毫米汞柱,非同一天测量、3次超标需及时就医。</p><p><br></p><p><span >2、清淡饮食</span></p><p>饮食上不要大鱼大肉,注意清淡一些,每天盐摄入量控制在6克以下,吃油量在25~30克之间。猪肉等红肉、高盐的腌制食品应限量,甜食和糖果尽量不吃。</p><p><span >3、控制体重</span></p><p>体质指数【BMI=体重(千克)÷身高的平方(米的平方)】尽可能保持在19~24之间。</p><p><span >4、补点钾</span></p><p>适当吃些钾含量较高的食物有助于降低血压,还能降低高血压患者发生中风的风险。建议多吃香蕉、桃子、西瓜、红薯、菠菜、芹菜、土豆等。</p><p><span >5、增加镁、钙的摄入</span></p><p>镁有助于血管扩张,补充镁的最安全方法是通过含镁丰富的食物来补充。富含镁的食物有各种干豆、鲜豆、香菇、菠菜、桂圆、豆芽等。钙不足也可使血压升高,钙增加会使血压下降。富含钙的食物有奶类、豆类等。</p><p><span >6、吃点醋</span></p><p>醋是通过抑制血管紧张素转换酶生成而直接抑制血压升高的。醋还具有利尿作用和有利于身体对钙的吸收,这些作用对降低血压有一定帮助。</p><p><span >7、抽空多走走</span></p><p>慢走5分钟、散步5分钟交替进行,可软化血管。建议利用零散时间锻炼,如出门少开车,多步行一会;养成晚饭后溜达一圈的习惯。</p><p><br></p><blockquote> <span >喝喝茶调血压</span></blockquote><p><span >葛 根 茶</span></p><p>葛根具有改善脑部血液循环之效,对因高血压引起的头痛、眩晕、耳鸣及腰酸腿痛等症状有较好的缓解功效。经常饮用葛根茶对治疗高血压具有明显的疗效。</p><p>做法:将葛根洗净切成薄片,每天30克,加水煮沸后当茶饮用。</p><p><span >莲子心茶</span></p><p>莲子心其味清苦,可以祛五脏之火,具有极好的降压去脂的功效。</p><p>做法:取莲子心12克,开水冲泡代茶饮,每天早晚各1次。</p><p><span >高血压是个终身性疾病,需要终身用药控制。但是除合理用药外,健康的生活方式必不可少。合理膳食、适量运动、戒烟限酒及心理健康,才是人类健康的四大基石。</span></p>"

//加载远程css 依赖网络速度
let string = "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0,user-scalable=no\"><meta content=\"yes\" name=\"apple-mobile-web-app-capable\"><meta content=\"yes\" name=\"apple-touch-fullscreen\"><meta name=\"format-detection\" content=\"telephone=no\"><link href=\"https://app.h5.ihaozhuo.com/lib/css/newscss.css\" rel=\"stylesheet\"></head><body>\(htmlContent)</body></html>"
self.webView.loadHTMLString(string, baseURL: nil)

//加载本地css
let link:String = Bundle.main.url(forResource: "newscss", withExtension: "css")!.absoluteString
let baseUrl = URL.init(fileURLWithPath: "file:///assets/")
let string = "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0,user-scalable=no\"><meta content=\"yes\" name=\"apple-mobile-web-app-capable\"><meta content=\"yes\" name=\"apple-touch-fullscreen\"><meta name=\"format-detection\" content=\"telephone=no\"><link href=\"" + link + "\" rel=\"stylesheet\"></head><body>\(htmlContent)</body></html>"
self.webView.loadHTMLString(string, baseURL: baseUrl)

代码挺简单的,看文章的小伙伴应该都能理解,其中主要区别就是这两句话,

//资源为网络路径
<link href=\"https://app.h5.ihaozhuo.com/lib/css/newscss.css\" rel=\"stylesheet\">
//资源为本地路径
<link href=\"" + link + "\" rel=\"stylesheet\">

可能有些小伙伴,加载本地资源不成功,其中需要两个点要注意:
1、就是需要设置baseURL。
2、baseUrl 可以写的我代码中完全一样,另外.css文件需要和webview在同级目录。

加载url地址

上面的那种加载H5的方法很简单粗暴,只需要替换网络连接为本地连接就好啦,但是我们项目实战中通过加载url地址的方式其实更常见些。这时候我们改怎么处理呢?修改资源为本地路径就别想啦,想破脑袋、查遍资料发现了这个好东西 WKURLSchemeHandler。不过这个东西在 iOS 11之后才有支持,不过iOS系统的更新占比完全不用担心的,直接搞起来。(可以通过添加 user-agent 来区分是否用指定的协议,H5遇到这个标识就使用customScheme,否则就是用原来的http或https)

先看看代码怎么写:

let configuration = WKWebViewConfiguration.init();
//这里是个注意点 yjkCustomScheme 这个玩意儿就是和H5约定的规则哦,H5加载的资源用这个作为协议就好啦
configuration.setURLSchemeHandler(CustomURLSchemeHandler.init(), forURLScheme: "yjkCustomScheme")
webView = WKWebView.init(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: view.frame.height), configuration: configuration)
webView.scrollView.decelerationRate = UIScrollView.DecelerationRate.normal
webView.navigationDelegate = self
self.view.addSubview(webView)
        
//***加载H5的地址就应该使用这个规则来加载,不然直接加载https的地址是无法进行拦截的
webView.load(URLRequest.init(url: URL.init(string: "yjkCustomScheme://app.h5.ihaozhuo.com/native/yjkdemo/index.html?v=6")!))

处理代理的完整代码:

//拦截类
class CustomURLSchemeHandler: NSObject {
    
}

extension CustomURLSchemeHandler:WKURLSchemeHandler{
    
    func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
        
        //文件名
        let fileName = urlSchemeTask.request.url?.lastPathComponent
        //文件扩展名(文件类型)
        let pathExtension = urlSchemeTask.request.url?.pathExtension
        
        //获取本地资源
        let fileURL = Bundle.main.url(forResource: fileName?.components(separatedBy: ".").first, withExtension: pathExtension)
        
        //本地存在文件
        if fileURL != nil{
            //获取本地资源异常
            do {
                //返回本地数据
                let data:NSData = try Data.init(contentsOf: fileURL!) as NSData
                let pathExtension = fileURL!.pathExtension
                let mime = mimeType(forPathExtension: pathExtension)
                
                let response:URLResponse = URLResponse.init(url: urlSchemeTask.request.url!, mimeType: mime, expectedContentLength: data.length, textEncodingName: nil)
                urlSchemeTask.didReceive(response)
                urlSchemeTask.didReceive(data as Data)
                urlSchemeTask.didFinish()
            }catch {
                //本地资源获取异常
                requestWebViewData(urlSchemeTask: urlSchemeTask)
            }
        }else{
            //无本地资源
            requestWebViewData(urlSchemeTask: urlSchemeTask)
        }
    }
    
    func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
        
    }
    
    /// 代替H5发出网络请求
    ///
    /// - Parameter urlSchemeTask:
    private func requestWebViewData(urlSchemeTask: WKURLSchemeTask){
        let schemeUrl:String = urlSchemeTask.request.url?.absoluteString ?? ""  //***这个搞过来好像都是小写的
        //换成原始的请求地址
        let replacedStr = schemeUrl.replacingOccurrences(of: "yjkcustomscheme", with: "https")
        //发出请求结果返回
        let request = URLRequest.init(url: URL.init(string: replacedStr)!)
        let config = URLSessionConfiguration.default
        let session = URLSession.init(configuration: config)
        let dataTask = session.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
            if error != nil{
                urlSchemeTask.didFailWithError(error!)
            }else{
                urlSchemeTask.didReceive(response!)
                urlSchemeTask.didReceive(data!)
                urlSchemeTask.didFinish()
            }
        }
        dataTask.resume()
    }
    
    /// 获取文件类型
    ///
    /// - Parameter pathExtension:
    /// - Returns:
    private func mimeType(forPathExtension pathExtension: String) -> String {
        if
            let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
            let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue()
        {
            return contentType as String
        }
        
        return "application/octet-stream"
    }
}

所有代码都在上面了,不多哔哔了。主要思想就是拦截掉H5发出去的请求,返回给他本地数据,从而减少H5网络请求的时长。

最后要说的就是这几点:
1、目前影响H5体验的主要因素就是网络请求的速度,在5G高速发展的今天,H5势必会成为将来发展的大趋势。
2、通过实测加载HTMLString比加载url的速度确实要快很多。因此,能通过后台返回Html标签的,尽量就使用Html标签。
3、本文只是介绍一个思路,要想完整应用的项目中,还是要做一些列的工作的。比如肯定是需要一个版本管理的机制的。

参考文章:
iOS WKWebView 加载本地HTML、CSS、JS文件
iOS app秒开H5优化总结
移动端本地 H5 秒开方案探索与实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容