跳到主要内容

WebView 优化实战

问题

App 内 WebView 加载慢、白屏时间长,如何优化?

答案

优化手段

手段说明收益
预创建 WebView 池App 启动时创建 WKWebView 实例减少 500ms+
离线包关键 H5 页面资源打包到 App 内减少网络耗时
预加载提前 loadRequest用户点击时秒开
DNS 预解析预连接 H5 域名减少 DNS 耗时
JSBridge 优化减少通信次数,批量传输减少交互延迟

WebView 池

class WebViewPool {
static let shared = WebViewPool()
private var pool: [WKWebView] = []

func warmUp(count: Int = 2) {
for _ in 0..<count {
let config = WKWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: config)
webView.loadHTMLString("", baseURL: nil) // 触发内核初始化
pool.append(webView)
}
}

func dequeue() -> WKWebView {
if let webView = pool.first {
pool.removeFirst()
return webView
}
return WKWebView()
}

func recycle(_ webView: WKWebView) {
webView.loadHTMLString("", baseURL: nil)
pool.append(webView)
}
}

离线包方案

// 拦截 WKWebView 请求,返回本地资源
class OfflineHandler: WKURLSchemeHandler {
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
guard let url = urlSchemeTask.request.url,
let localData = loadLocalResource(for: url) else {
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
return
}
let response = URLResponse(url: url, mimeType: mimeType(for: url),
expectedContentLength: localData.count, textEncodingName: "utf-8")
urlSchemeTask.didReceive(response)
urlSchemeTask.didReceive(localData)
urlSchemeTask.didFinish()
}
}

常见面试问题

Q1: WKWebView 和 UIWebView 的区别?

答案:WKWebView 运行在独立进程,内存占用不算在 App 进程内,不易 OOM。UIWebView 已在 iOS 12 废弃。WKWebView 支持 WKURLSchemeHandler 自定义协议拦截,但不支持 NSURLProtocol

相关链接