web離線技術(shù)原理

注:本篇研究重點(diǎn)不在于某個(gè)離線方案的具體使用,而在于對(duì)方案的優(yōu)缺點(diǎn)分析、探究和選型,以及一些我個(gè)人的看法。

前言

web離線技術(shù)顧名思義就是將H5/CSS/JS和資源文件打包提前下發(fā)到App中,這樣App在加載網(wǎng)頁(yè)的時(shí)候?qū)嶋H上加載的是本地的文件,減少網(wǎng)絡(luò)請(qǐng)求來(lái)提高網(wǎng)頁(yè)的渲染速度,并實(shí)現(xiàn)動(dòng)態(tài)更新效果。

就目前情況來(lái)看,離線包的方案也是層出不窮的,本篇將列舉市面最常見(jiàn)的四種離線方案,進(jìn)行探討分析,選擇最優(yōu)方案構(gòu)建離線包功能。如果你有優(yōu)化h5渲染速度的需求,可以用來(lái)參考,本篇僅做技術(shù)選型和方案原理刨析,后續(xù)篇章會(huì)選出最優(yōu)方案進(jìn)行深入探討,加具體實(shí)現(xiàn)。目錄部分為后續(xù)延伸。

方案

  1. 通過(guò)獲取沙盒H5路徑直接加載
  2. 基于NSURLProtocol進(jìn)行請(qǐng)求攔截
  3. 基于WKURLSchemeHandler進(jìn)行自定義scheme注冊(cè)攔截
  4. 起本地服務(wù)器加載本地資源

選型

方案一:通過(guò)獲取沙盒H5路徑直接加載

直接加載本地h5,大名鼎鼎的《cordova》框架便是基于此實(shí)現(xiàn)。

  • 1.將所有的h5文件都放入一個(gè)文件夾中。

  • 2.將這個(gè)文件夾以相對(duì)路徑的方式倒入到工程代碼中。

  • 3.獲取本地的文件路徑。

這個(gè)方案就是將部署在服務(wù)器上面的前端代碼直接解壓到本地沙盒。加載js的時(shí)候直接加載本地沙盒中的html進(jìn)行離線加載。將每個(gè)前端的模塊都定義為一個(gè)應(yīng)用,打上id下發(fā)給客戶端,當(dāng)用戶點(diǎn)擊對(duì)應(yīng)模塊的時(shí)候根據(jù)id去沙盒查找對(duì)應(yīng)的離線資源進(jìn)行加載實(shí)現(xiàn)秒開(kāi)。

  • 優(yōu)點(diǎn):簡(jiǎn)單。
  • 缺點(diǎn):
image
    1. 實(shí)際上從截圖中可以看到,我們?cè)谠L問(wèn)本地html的時(shí)候可以看到實(shí)際路徑為file:///.../index.html。這是在使用file協(xié)議訪問(wèn)html,有些html樣式并不支持file協(xié)議,在樣式和功能上會(huì)有缺失,還會(huì)有一些api上的差異,前端開(kāi)發(fā)好的代碼可能下載到沙盒里導(dǎo)致有些資源無(wú)法使用,產(chǎn)生一些適配問(wèn)題。
    1. 訪問(wèn)本地資源還會(huì)導(dǎo)致資源路徑泄漏產(chǎn)生安全問(wèn)題。
    1. 還會(huì)有一些瀏覽器的安全設(shè)置無(wú)法通過(guò)。
    1. 無(wú)法實(shí)現(xiàn)跨域資源請(qǐng)求,會(huì)讓前端開(kāi)發(fā)人員無(wú)法訪問(wèn)外部cdn。

file協(xié)議&http協(xié)議:file協(xié)議主要用于訪問(wèn)本地計(jì)算機(jī)中的文件,好比通過(guò)資源管理器打開(kāi)文件一樣,針對(duì)本地的,即file協(xié)議是訪問(wèn)你本機(jī)的文件資源。http協(xié)議訪問(wèn)本地html是在本地起了一臺(tái)http服務(wù)器,然后你訪問(wèn)自己電腦上的本地服務(wù)器,http服務(wù)器再去訪問(wèn)你本機(jī)的文件資源。

瀏覽器對(duì)兩種協(xié)議的處理有時(shí)會(huì)不同,譬如某些網(wǎng)頁(yè)中直接調(diào)用file協(xié)議來(lái)打開(kāi)圖片,這樣的功能會(huì)被瀏覽器的安全設(shè)置阻擋,因?yàn)槟J(rèn)上,html是運(yùn)行于客戶端的超文本語(yǔ)言,從安全性上來(lái)講,服務(wù)端不能對(duì)客戶端進(jìn)行本地操作。即使有一些象cookie這類的本地操作,也是需要進(jìn)行安全級(jí)別設(shè)置的。倘若你需要載入外部cdn的資源,比如livereload、browserSync等工具的使用,由于瀏覽器的同源策略,從本地文件系統(tǒng)載入外部文件將會(huì)失敗,會(huì)拋出安全性異常。

總的來(lái)說(shuō),這個(gè)方案會(huì)對(duì)前端產(chǎn)生嚴(yán)重的入侵,限制了前端只能通過(guò)相對(duì)路徑對(duì)js,css,image等資源的加載,還有file協(xié)議的跨域問(wèn)題導(dǎo)致無(wú)法引入外部cdn,這樣會(huì)限制前端開(kāi)發(fā),雖然用起來(lái)最簡(jiǎn)單,但這并不是一個(gè)好的方案。

方案二:基于NSURLProtocol進(jìn)行請(qǐng)求攔截

既然直接加載本地資源文件不是最好方案,那我們是否可以考慮一下另一種方案基于NSURLProtocol攔截呢?當(dāng)然可行了,但是往下看:

UIWebView上,protocol攔截確實(shí)是我們的首選方案,創(chuàng)建個(gè)子類,在子類里面實(shí)現(xiàn)protocol的代理方法即可實(shí)現(xiàn)對(duì)所有請(qǐng)求的攔截,當(dāng)然也包括html里面對(duì)css、js、img等資源加載的請(qǐng)求。

- (void)startLoading
{
    
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    if (mimeType == nil) {
        mimeType = @"text/plain";
    }

    NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : mimeType}];

    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    if (data != nil) {
        [[self client] URLProtocol:self didLoadData:data];
    }
    [[self client] URLProtocolDidFinishLoading:self];
}

這樣即可完美解決h5的資源請(qǐng)求問(wèn)題。

那么在WKWebView上,這個(gè)方案是行不通的,關(guān)于這方面的解釋已經(jīng)很多了,WKWebView在獨(dú)立于app進(jìn)程之外的進(jìn)程中執(zhí)行網(wǎng)絡(luò)請(qǐng)求,請(qǐng)求數(shù)據(jù)不經(jīng)過(guò)主進(jìn)程,因此,在WKWebView上直接使用 NSURLProtocol 無(wú)法攔截請(qǐng)求。當(dāng)然通過(guò)私有api可以解決問(wèn)題:

//僅iOS8.4以上可用
Class cls = NSClassFromString(@"WKBrowsingContextController”); 
SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");

if ([(id)cls respondsToSelector:sel]) {
     #pragma clang diagnostic push
     #pragma clang diagnostic ignored "-Warc-performSelector-leaks"

         // 注冊(cè)http(s) scheme, 把 http和https請(qǐng)求交給 NSURLProtocol處理 
        [(id)cls performSelector:sel withObject:@"http"];
         [(id)cls performSelector:sel withObject:@"https"];

    #pragma clang diagnostic pop
     }
}

但依然存在缺陷,post請(qǐng)求body數(shù)據(jù)被清空。由于WKWebView在獨(dú)立進(jìn)程里執(zhí)行網(wǎng)絡(luò)請(qǐng)求。一旦注冊(cè)http(s) scheme后,網(wǎng)絡(luò)請(qǐng)求將從Network Process發(fā)送到App Process,這樣 NSURLProtocol 才能攔截網(wǎng)絡(luò)請(qǐng)求。在webkit2的設(shè)計(jì)里使用MessageQueue進(jìn)行進(jìn)程之間的通信,Network Process會(huì)將請(qǐng)求encode成一個(gè)Message,然后通過(guò) IPC 發(fā)送給 App Process。出于性能的原因,encode的時(shí)候HTTPBody和HTTPBodyStream這兩個(gè)字段被丟棄掉了。

如果使用Get請(qǐng)求攔截離線資源是沒(méi)有問(wèn)題的,攔截到請(qǐng)求后映射為本地資源生成NSHTTPURLResponse* response,像上面的方案一樣去處理就可以了。但是使用私有API又會(huì)面臨另外一個(gè)風(fēng)險(xiǎn):被拒。

說(shuō)一點(diǎn)題外話,目前據(jù)我所了解到百度App安卓就是采用的請(qǐng)求攔截方式,但是,是安卓,看下圖:


image

圖片來(lái)源《百度APP-Android H5首屏優(yōu)化實(shí)踐》

通過(guò)上圖可以分析第11、12步,WebView對(duì)html解析的時(shí)候可以發(fā)現(xiàn)資源請(qǐng)求并攔截,返回對(duì)應(yīng)的緩存資源并渲染。實(shí)際上這個(gè)方案在iOS上是行不通的,安卓可以使用自家瀏覽器,可以魔改瀏覽器,比如支付寶的UC,百度的T7等。iOS應(yīng)用內(nèi)是不允許使用魔改瀏覽器的,很遺憾,也就是說(shuō)蘋(píng)果爸爸開(kāi)放了什么,我們才能使用什么。

總結(jié)來(lái)說(shuō),這個(gè)方案并不會(huì)對(duì)前端產(chǎn)生入侵,前端依然可以不需要任何改變按部就班開(kāi)發(fā)就好了。但對(duì)于body的攔截和對(duì)私有api的使用,依然是存在風(fēng)險(xiǎn),但是據(jù)我所知這個(gè)方案也是有項(xiàng)目在使用的,所以選則推薦。

方案三:基于WKURLSchemeHandler進(jìn)行自定義scheme注冊(cè)攔截

WKURLSchemeHandler是iOS11就推出的,用于處理自定義請(qǐng)求的方案,不過(guò)并不能處理Http、Https等常規(guī)scheme。

WKWebViewConfiguration開(kāi)放了setURLSchemeHandler:forURLScheme:函數(shù),需要指定一個(gè)自定義的scheme和一個(gè)用來(lái)處理WKURLSchemeHandler回調(diào)的自定義對(duì)象。

根據(jù)注釋來(lái)看,如果注冊(cè)了一個(gè)無(wú)效的scheme或者使用WebKit內(nèi)部已經(jīng)處理的scheme,例如http、https、file等將會(huì)引發(fā)異常。我們最好使用WKWebView的handlesURLScheme:類方法來(lái)檢查給定scheme的可用性,以免帶來(lái)一些未知問(wèn)題。
使用方法也很簡(jiǎn)單:

if (@available(iOS 11.0, *)) {
        BOOL allowed = [WKWebView handlesURLScheme:@""];
        if (allowed) {
            WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
            //設(shè)置URLSchemeHandler來(lái)處理特定URLScheme的請(qǐng)求,CustomURLSchemeHandler需要實(shí)現(xiàn)WKURLSchemeHandler協(xié)議,用來(lái)攔截customScheme的請(qǐng)求。
            [configuration setURLSchemeHandler:[CustomURLSchemeHandler new] forURLScheme: @"customScheme"];
            WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
            self.view = webView;
            [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"customScheme://"]]];
        }
    } else {
        // Fallback on earlier versions
    }

WKURLSchemeHandler提供了兩個(gè)回調(diào)函數(shù)由上面自定義的CustomURLSchemeHandler對(duì)象來(lái)處理:

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask;
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask;

通過(guò)urlSchemeTaskrequest對(duì)象可以拿到請(qǐng)求對(duì)應(yīng)的url,如果是我們自定義的scheme就去攔截它,通過(guò)url映射到對(duì)應(yīng)的本地資源,并加載本地資源。

如果本地資源不存在,那么通過(guò)url直接構(gòu)建request對(duì)象訪問(wèn)服務(wù)器,如果本地資源存在,那么就可以直接加載本地資源,和第二個(gè)方案一樣去使用它:

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
    NSString *urlString = urlSchemeTask.request.URL.absoluteString;
    //定位本地資源并映射到本地資源地址 filePath
    
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : @"text/plain"}];
    [urlSchemeTask didReceiveResponse:response];
    [urlSchemeTask didReceiveData:data];
    [urlSchemeTask didFinish];
}

實(shí)際上這個(gè)方案很好的解決了資源攔截的問(wèn)題,并且能像第二個(gè)方案一樣去做處理??雌饋?lái)沒(méi)什么問(wèn)題。但是它依然有短板:

    1. 因?yàn)槭褂玫淖远xscheme,并不是http協(xié)議,所以它依然無(wú)法解決跨域問(wèn)題。
    1. 由于自定義了scheme,對(duì)于前端來(lái)說(shuō),需要額外將scheme設(shè)置為我們自定義的customScheme,這又會(huì)給前端帶來(lái)大量的改造,所以對(duì)前端還是產(chǎn)生了入侵。
    1. 上面提到在安卓完全不需要像iOS這樣大費(fèi)周章的繞彎路,所以安卓可能就不需要這個(gè)自定義的scheme,這樣又會(huì)導(dǎo)致面臨著與安卓差異化嚴(yán)重問(wèn)題。
    1. 因?yàn)锳PI的限制,只能支持iOS11之后的系統(tǒng)。

所以這樣來(lái)看,WKURLSchemeHandler的攔截方案也并不是很友好。

方案四:起本地服務(wù)器加載本地資源

根據(jù)支付寶的文章《支付寶移動(dòng)端動(dòng)態(tài)化方案實(shí)踐》對(duì)離線包的描述:

當(dāng) H5 容器發(fā)出資源請(qǐng)求時(shí),其訪問(wèn)本地資源或線上資源所使用的 URL 是一致的。H5 容器會(huì)先截獲該請(qǐng)求,截獲請(qǐng)求后,發(fā)生如下情況:

1.如果本地有資源可以滿足該請(qǐng)求的話,H5 容器會(huì)使用本地資源。

2.如果沒(méi)有可以滿足請(qǐng)求的本地資源,H5 容器會(huì)使用線上資源。 因此,無(wú)論資源是在本地或者是線上,WebView 都是無(wú)感知的。

可以看出,支付寶并不是采用的上述三種方案,因?yàn)樯鲜龇桨赋藀rotocol攔截以外,都無(wú)法做到讓WebView無(wú)感知,據(jù)我所知,支付寶目前應(yīng)該采用的是起本地服務(wù)器方案。起本地服務(wù)器自然就是http協(xié)議了,http協(xié)議和本地的file協(xié)議差異第一種方案里面已經(jīng)做了詳細(xì)介紹,那么如果能夠使用http協(xié)議加載本地資源的話,這樣做能夠最大程度的讓前端對(duì)于離線包“無(wú)感”,也就是說(shuō)前端不需要修改scheme,不需要考慮會(huì)不會(huì)因?yàn)閒ile協(xié)議而帶來(lái)一些問(wèn)題,也能忽略掉攔截api的平臺(tái)差異導(dǎo)致的框架實(shí)現(xiàn)差異,這樣一來(lái)前端開(kāi)發(fā)好的代碼一份即可,布在服務(wù)器的同時(shí),也上傳到我們的離線包平臺(tái)就OK了。所以稱之為“無(wú)感知”。

  • 優(yōu)點(diǎn):優(yōu)點(diǎn)前面都說(shuō)了,同網(wǎng)絡(luò)服務(wù)器加載的樣式和功能完全一致,不入侵前端,前端并不用關(guān)心當(dāng)前頁(yè)面是離線還是非離線,做到最大無(wú)感知。當(dāng)然有優(yōu)點(diǎn)就有缺點(diǎn),這也并不是一個(gè)完美方案。

  • 缺點(diǎn):

      1. 需要額外搭建本地服務(wù)器,html文件的路徑需要做處理。
      1. 對(duì)于本地服務(wù)器的搭建存在成本問(wèn)題,本地服務(wù)器的管理問(wèn)題,例如服務(wù)器的打開(kāi)、關(guān)閉時(shí)機(jī)等等。
      1. 對(duì)于本地服務(wù)器會(huì)不會(huì)帶來(lái)其他問(wèn)題對(duì)于我來(lái)說(shuō)也是未知的,并不是所有團(tuán)隊(duì)都能像支付寶一樣搭建一個(gè)自己的服務(wù)器來(lái)處理。

這個(gè)方案的實(shí)施可以參考:《基于 LocalWebServer 實(shí)現(xiàn) WKWebView 離線資源加載》的處理,但是文末也提到了幾個(gè)問(wèn)題:

  • 資源訪問(wèn)權(quán)限安全問(wèn)題。
  • APP前后臺(tái)切換時(shí),服務(wù)重啟性能耗時(shí)問(wèn)題。
  • 服務(wù)運(yùn)行時(shí),電量及CPU占有率問(wèn)題。
  • 多線程及磁盤(pán)IO問(wèn)題。

這些問(wèn)題對(duì)于我來(lái)說(shuō)也是未知的。如果有成熟的搭建本地服務(wù)器方案歡迎留言。

本篇旨在分析一條最優(yōu)方案來(lái)構(gòu)建離線包核心功能,但是因?yàn)橛行』锇樘岢鲆恍╊A(yù)加載等優(yōu)化問(wèn)題,所以從`bang's`的博客中摘了幾條優(yōu)化方案可供參考。

Fallback 技術(shù)

題外話:從上面提到的支付寶文章來(lái)看,還有一段我們可以分析一下:

為了解決離線包不可用的場(chǎng)景,fallback 技術(shù)應(yīng)運(yùn)而生。每個(gè)離線包發(fā)布的時(shí)候,都會(huì)同步在 CDN 發(fā)布一個(gè)對(duì)應(yīng)的線上版本,目錄結(jié)構(gòu)和離線包結(jié)構(gòu)一致。fallback 地址會(huì)隨離線包信息下發(fā)到本地。在離線包沒(méi)有下載好的場(chǎng)景下,客戶端會(huì)攔截頁(yè)面請(qǐng)求,轉(zhuǎn)向?qū)?yīng)的 CDN 地址, 實(shí)現(xiàn)在線頁(yè)面和離線頁(yè)面隨時(shí)切換。

這個(gè)不可用場(chǎng)景應(yīng)該就是離線包不可用,未更新,資源有損壞,md5不匹配或者驗(yàn)簽不通過(guò)等等。

    1. 如果本地離線包沒(méi)有或不是最新,就同步阻塞等待下載最新離線包。這種方案用戶體驗(yàn)最差,因?yàn)殡x線包體積相對(duì)較大。
    1. 如果本地有舊包,用戶本次就直接使用舊包,如果沒(méi)有再同步阻塞等待,這種會(huì)導(dǎo)致更新不及時(shí),無(wú)法確保用戶使用最新版本。(據(jù)我所知微信小程序?yàn)榇朔桨福?/li>
    1. 對(duì)離線包做一個(gè)線上版本,離線包里的文件在服務(wù)端有一一對(duì)應(yīng)的訪問(wèn)地址,在本地沒(méi)有離線包時(shí),直接訪問(wèn)對(duì)應(yīng)的線上地址,跟傳統(tǒng)打開(kāi)一個(gè)在線頁(yè)面一樣,這種體驗(yàn)相對(duì)等待下載整個(gè)離線包較好,也能保證用戶訪問(wèn)到最新。

第三種方案應(yīng)該就是支付寶的fallback 技術(shù),可以解決上述問(wèn)題。當(dāng)然前兩種方案也不是不可取,還是要看需求和場(chǎng)景。

公共資源包

每個(gè)包都會(huì)使用相同的 JS 框架和 CSS 全局樣式,這些資源重復(fù)在每一個(gè)離線包出現(xiàn)太浪費(fèi),可以做一個(gè)公共資源包提供這些全局文件。

預(yù)加載 webview

無(wú)論是 iOS 還是 Android,本地 Webview 初始化都要不少時(shí)間,可以預(yù)先初始化好 Webview。這里分兩種預(yù)加載:

首次預(yù)加載:在一個(gè)進(jìn)程內(nèi)首次初始化 Webview 與第二次初始化不同,首次會(huì)比第二次慢很多。原因預(yù)計(jì)是 Webview 首次初始化后,即使 Webview 已經(jīng)釋放,但一些多 Webview 共用的全局服務(wù)或資源對(duì)象仍沒(méi)有釋放,第二次初始化時(shí)不需要再生成這些對(duì)象從而變快。我們可以在 APP 啟動(dòng)時(shí)預(yù)先初始化一個(gè) Webview 然后釋放,這樣等用戶真正走到 H5 模塊去加載 Webview時(shí)就變快了。

Webview 池:可以用兩個(gè)或多個(gè) Webview 重復(fù)使用,而不是每次打開(kāi) H5 都新建 webview。不過(guò)這種方式要解決頁(yè)面跳轉(zhuǎn)時(shí)清空上一個(gè)頁(yè)面,另外若一個(gè) H5 頁(yè)面上 JS 出現(xiàn)內(nèi)存泄漏,就影響到其他頁(yè)面,在 APP 運(yùn)行期間都無(wú)法釋放了。

預(yù)加載數(shù)據(jù)

理想情況下離線包的方案第一次打開(kāi)時(shí)所有HTML/JS/CSS 都使用本地緩存,無(wú)需等待網(wǎng)絡(luò)請(qǐng)求,但頁(yè)面上的用戶數(shù)據(jù)還是需要實(shí)時(shí)拉,這里可以做個(gè)優(yōu)化,在 Webview 初始化的同時(shí)并行去請(qǐng)求數(shù)據(jù),Webview初始化是需要一些時(shí)間的,這段時(shí)間沒(méi)有任何網(wǎng)絡(luò)請(qǐng)求,在這個(gè)時(shí)機(jī)并行請(qǐng)求可以節(jié)省不少時(shí)間。

具體實(shí)現(xiàn)上,首先可以在配置表注明某個(gè)離線包需要預(yù)加載的 URL,客戶端在 Webview 初始化同時(shí)發(fā)起請(qǐng)求,請(qǐng)求由一個(gè)管理器管理,請(qǐng)求完成時(shí)緩存結(jié)果,然后 Webview 在初始化完畢后開(kāi)始請(qǐng)求剛才預(yù)加載的 URL,客戶端攔截到請(qǐng)求,轉(zhuǎn)接到剛才提到的請(qǐng)求管理器,若預(yù)加載已完成就直接返回內(nèi)容,若未完成則等待。

使用客戶端接口

網(wǎng)路和存儲(chǔ)接口如果使用 webkit 的 ajax 和 localStorage 會(huì)有不少限制,難以優(yōu)化,可以在客戶端提供這些接口給 JS,客戶端可以在網(wǎng)絡(luò)請(qǐng)求上做像 DNS 預(yù)解析/IP直連/長(zhǎng)連接/并行請(qǐng)求等更細(xì)致的優(yōu)化,存儲(chǔ)也使用客戶端接口也能做讀寫(xiě)并發(fā)/用戶隔離等針對(duì)性優(yōu)化。
服務(wù)端渲染
早期 web 頁(yè)面里,JS 只是負(fù)責(zé)交互,所有內(nèi)容都是直接在 HTML 里,到現(xiàn)代 H5 頁(yè)面,很多內(nèi)容已經(jīng)依賴 JS 邏輯去決定渲染什么,例如等待 JS 請(qǐng)求 JSON 數(shù)據(jù),再拼接成 HTML 生成 DOM 渲染到頁(yè)面上,于是頁(yè)面的渲染展現(xiàn)就要等待這一整個(gè)過(guò)程,這里有一個(gè)耗時(shí),減少這里的耗時(shí)也是白屏優(yōu)化的范圍之內(nèi)。
優(yōu)化方法可以是人為減少 JS 渲染邏輯,也可以是更徹底地,回歸到原始,所有內(nèi)容都由服務(wù)端返回的 HTML 決定,無(wú)需等待 JS 邏輯,稱之為服務(wù)端渲染。是否做這種優(yōu)化視業(yè)務(wù)情況而定,畢竟這種會(huì)帶來(lái)開(kāi)發(fā)模式變化/流量增大/服務(wù)端開(kāi)銷(xiāo)增大這些負(fù)面影響。手Q的部分頁(yè)面就是使用服務(wù)端渲染的方式,稱為動(dòng)態(tài)直出。

總結(jié)

關(guān)于這四種方案,都有優(yōu)劣,關(guān)于選型,我偏向于NSURLProtocol攔截起本地服務(wù)器的方案。當(dāng)然還是要參照自己的需求,就應(yīng)用來(lái)說(shuō),都是可以的。當(dāng)然對(duì)于一個(gè)優(yōu)秀的Hybird框架,這些還是遠(yuǎn)遠(yuǎn)不夠的,不管是從支付寶的方案還是手百的方案來(lái)看,需要做的優(yōu)化還有很多,不管是手Q的動(dòng)態(tài)直出,還是支付寶的Nebula,都還有很多東西需要我們探討學(xué)習(xí)。不知道大家有沒(méi)有發(fā)現(xiàn),不只是手百,包括頭條,騰訊新聞,在頁(yè)面沒(méi)有全部push出之前就已經(jīng)渲染完畢了,說(shuō)明都存在對(duì)h5頁(yè)面進(jìn)行預(yù)加載的處理,這也是值得我們深入探討的環(huán)節(jié)。當(dāng)然這一塊還要視具體需求和人力來(lái)定了。關(guān)于離線包的處理,這是我目前能想到的所有方案,對(duì)于他們的優(yōu)劣也有總結(jié),如果你有什么建議或者更好的方案,歡迎留言。

開(kāi)源地址:《WKJavaScriptBridge》(離線包后續(xù)引入)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,689評(píng)論 1 32
  • 導(dǎo)語(yǔ) WKWebView 是蘋(píng)果在 WWDC 2014 上推出的新一代 webView 組件,用以替代 UIKit...
    Jecky丶閱讀 8,758評(píng)論 2 22
  • 1、WKWebView 白屏問(wèn)題WKWebView 自詡擁有更快的加載速度,更低的內(nèi)存占用,但實(shí)際上 WKWebV...
    iosRn閱讀 2,177評(píng)論 1 10
  • 轉(zhuǎn)載鏈接:騰訊Bugly 導(dǎo)語(yǔ) WKWebView 是蘋(píng)果在 WWDC 2014 上推出的新一代 webView ...
    Jelly_沫閱讀 2,931評(píng)論 0 3
  • WKWebView 是蘋(píng)果在 WWDC 2014 上推出的新一代 webView 組件,用以替代 UIKit 中笨...
    Aiana閱讀 4,829評(píng)論 1 8

友情鏈接更多精彩內(nèi)容