深入解析 WKWebView 的 didFinish 回调时机:页面加载与异步操作的处理

在 iOS 开发中,我们经常会用 WKWebView 来加载和展示 H5 页面。通常,开发者会在 WKWebViewdidFinish 方法中处理页面加载完成后的逻辑,例如更新 UI 或执行后续操作。然而,didFinish 的触发时机并不总是如我们所期待,它并不会等待所有异步操作(如 AJAX 请求、图片加载等)完成后再执行。

本文将深入探讨 didFinish 的触发时机及其受哪些加载内容的影响,同时提供一种方法,确保页面在所有异步操作完成后再触发回调通知 iOS。


WKWebView 的 didFinish 回调触发时机

在 iOS 的 WKWebView 中,didFinish 回调的触发代表了页面 HTML 文档的初步加载完成。这意味着浏览器已经解析并加载了页面的主要内容结构,但并未考虑页面中的所有资源(如图片、CSS、JavaScript、AJAX 请求等)是否加载完毕。因此,理解 didFinish 的触发时机及其受哪些加载内容影响,对合理设计页面加载完成的处理逻辑至关重要。

以下是 didFinish 回调受影响的几种内容:

  1. HTML 文档的解析和加载

    didFinish 回调会在 HTML 文档加载完成后触发。也就是说,当 WKWebView 获取到并渲染了页面的初始 HTML 结构后,didFinish 就会被触发。这一过程不包括后续的资源加载,因此只代表页面的基础结构已加载完成,而非页面的所有内容。

  2. CSS 和 JavaScript 文件的加载

    外部 CSS 和 JavaScript 文件的加载也会影响 didFinish 的触发,特别是外部 CSS 的加载。浏览器在初步渲染页面时需要完整的样式信息,因此通常会等待 CSS 文件的加载完成再触发 didFinish

    同样地,对于页面中同步加载的 JavaScript(没有 asyncdefer 标记的脚本),浏览器会在加载并执行完脚本后才触发 didFinish。然而,异步加载的 JavaScript 则不影响该回调,它们的执行可能在 didFinish 触发后继续进行。

  3. 图片和其他媒体资源的加载

    页面中的图片、视频和音频等媒体资源的加载时间不影响 didFinish 的触发。即使图片还未完全加载,didFinish 依然会在 HTML 和 CSS 加载完成后触发。因此,如果页面中有大量的图片或其他媒体文件,didFinish 回调会提前触发,而图片加载可能仍在进行。

  4. AJAX 请求和动态内容加载

    许多现代 H5 页面会在加载后发起 AJAX 请求以获取动态数据。didFinish 回调不会等待 AJAX 请求完成,即使这些请求的数据对页面显示至关重要。也就是说,页面在视觉上可能看似完成加载,但实际数据尚未加载完毕。因此,若希望在 AJAX 请求完成后再触发回调,必须通过额外的手段进行控制。

  5. <iframe> 和嵌套资源的加载

    如果页面中包含 <iframe> 元素,didFinish 会在该 iframe 的内容加载完成后触发。嵌套的 iframe 也是如此,didFinish 只会在最外层框架的内容加载完成时触发。然而,嵌套框架中的进一步异步加载操作,如同 AJAX 请求和媒体加载一样,也不会影响 didFinish 的触发。


如何确保页面所有异步操作完成后再触发 iOS 回调

由于 didFinish 回调的触发不依赖于页面的所有资源加载和异步操作完成,若我们希望在页面的所有异步请求和资源加载完毕后再通知 iOS 端,可以通过以下方法实现:

  1. 在 H5 页面中实现异步操作的完成检测
    可以在页面中使用 JavaScript 控制异步操作的完成。例如,使用 Promise.all 等手段确保页面中的所有 AJAX 请求完成后触发一个事件,表示页面所有内容已经加载完毕。

  2. 使用 JavaScript 向 iOS 端发送消息
    在 H5 页面所有异步操作完成后,通过 window.webkit.messageHandlers 向 iOS 发送消息,以便触发 iOS 的相应回调,从而达到“所有资源加载完成后再通知 iOS”的效果。

代码示例

以下是一个完整的实现示例:

1. H5 页面代码(JavaScript)

在页面中使用 Promise.all 来监听所有异步操作完成后再通知 iOS:

function onAllAsyncOperationsComplete() {
    if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.notifyCompletion) {
        window.webkit.messageHandlers.notifyCompletion.postMessage("all_done");
    }
}

// 假设页面有多个 AJAX 请求
Promise.all([
    fetch('https://example.com/api/data1'),
    fetch('https://example.com/api/data2'),
    // 其他异步操作
]).then(responses => {
    // 确保所有异步操作完成后调用 onAllAsyncOperationsComplete
    onAllAsyncOperationsComplete();
}).catch(error => {
    console.error("异步操作失败:", error);
});
2. iOS 端代码(Swift)

在 iOS 端,通过注册 JavaScript 消息处理来接收通知,并在接收到通知后执行后续操作:

import WebKit

class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
    
    var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let contentController = WKUserContentController()
        contentController.add(self, name: "notifyCompletion")  // 注册 JavaScript 通知
        
        let config = WKWebViewConfiguration()
        config.userContentController = contentController
        
        webView = WKWebView(frame: self.view.bounds, configuration: config)
        webView.navigationDelegate = self
        self.view.addSubview(webView)
        
        if let url = URL(string: "https://yourwebsite.com") {
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        // 在页面加载完成后执行其他逻辑
        print("页面加载完成")
    }
    
    // 处理 JavaScript 中发送的通知
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "notifyCompletion" {
            if let body = message.body as? String, body == "all_done" {
                print("所有异步操作已完成,触发回调")
                // 执行你希望在所有异步操作完成后执行的代码
            }
        }
    }
}

总结

WKWebViewdidFinish 回调在 HTML 文档结构加载完成后触发,但不会等待页面中的所有异步操作和资源加载完成。因此,为了确保在页面全部资源加载完毕后再通知 iOS,可以通过 H5 页面中的 JavaScript 控制异步加载的完成状态,并使用 window.webkit.messageHandlers 与 iOS 端通信,从而实现更准确的加载完成时机控制。

posted @ 2024-11-07 16:19  BuddyLiu  阅读(128)  评论(0编辑  收藏  举报