web離線技術(shù)原理

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

前言

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

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

方案

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

選型

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

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

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

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

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

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

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

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

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

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

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

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

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

- (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的資源請求問題。

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

//僅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"

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

    #pragma clang diagnostic pop
     }
}

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

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

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


image

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

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

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

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

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

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

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

if (@available(iOS 11.0, *)) {
        BOOL allowed = [WKWebView handlesURLScheme:@""];
        if (allowed) {
            WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
            //設(shè)置URLSchemeHandler來處理特定URLScheme的請求,CustomURLSchemeHandler需要實(shí)現(xiàn)WKURLSchemeHandler協(xié)議,用來攔截customScheme的請求。
            [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對象來處理:

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

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

如果本地資源不存在,那么通過url直接構(gòu)建request對象訪問服務(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è)方案很好的解決了資源攔截的問題,并且能像第二個(gè)方案一樣去做處理。看起來沒什么問題。但是它依然有短板:

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

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

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

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

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

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

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

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

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

  • 缺點(diǎn):

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

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

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

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

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

Fallback 技術(shù)

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

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

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

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

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

公共資源包

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

預(yù)加載 webview

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

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

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

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

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

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

使用客戶端接口

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

總結(jié)

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

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

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

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

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

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