WKWebView 加载速度优化

作者: 忆辰念家 | 来源:发表于2019-06-06 14:20 被阅读2次

    因为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系统的更新占比完全不用担心的,直接搞起来。

    先看看代码怎么写:

    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 秒开方案探索与实现

    相关文章

      网友评论

        本文标题:WKWebView 加载速度优化

        本文链接:https://www.haomeiwen.com/subject/qweyxctx.html