Swift同步获取JS执行后页面完整HTML的实现问题
问题分析与解决方案
代码卡顿的核心原因
- 执行时机错误:调用
webView.load后立刻执行evaluateJavaScript,此时页面还未开始加载,且你的脚本仅注册了window.onload事件,并未立即返回HTML内容,导致evaluateJavaScript的回调永远不会触发,信号量一直处于等待状态,直接阻塞主线程造成卡顿。 - 脚本逻辑无效:
window.onload内部仅写了document.body.innerHTML,但该表达式未被返回,即便页面加载完成,也无法把HTML内容传递给evaluateJavaScript的回调。 - WebView未挂载:WKWebView没有添加到视图层级中,部分场景下无法正常完成页面加载和JS执行流程。
修正后的代码
import WebKit class InternetLoader: NSObject, WKNavigationDelegate { private let webView: WKWebView private var semaphore: DispatchSemaphore? private var resultHtml: String? override init() { let config = WKWebViewConfiguration() webView = WKWebView(frame: .zero, configuration: config) super.init() webView.navigationDelegate = self // 将WebView临时添加到窗口,确保能正常加载页面 if let window = UIApplication.shared.windows.first { window.addSubview(webView) } } func getHTML(from url: URL) -> String { semaphore = DispatchSemaphore(value: 0) resultHtml = nil webView.load(URLRequest(url: url)) semaphore!.wait() // 兜底处理,避免强制解包崩溃 return resultHtml ?? "" } // 页面加载完成后执行JS获取完整HTML func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // 获取整个页面的完整HTML,而非仅body部分 let script = "document.documentElement.outerHTML;" webView.evaluateJavaScript(script) { [weak self] result, error in guard let self = self else { return } if let error = error { print("执行JS出错:\(error.localizedDescription)") } else { self.resultHtml = result as? String } // 发送信号解除阻塞 self.semaphore?.signal() self.semaphore = nil // 移除WebView,避免内存泄漏 self.webView.removeFromSuperview() } } // 处理页面加载失败的情况,防止信号量一直等待 func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { print("页面加载失败:\(error.localizedDescription)") semaphore?.signal() semaphore = nil webView.removeFromSuperview() } } // 使用示例 let il = InternetLoader() let html = il.getHTML(from: URL(string: "https://stackoverflow.com/questions")!)
关键修正点
- 实现
WKNavigationDelegate,通过didFinish方法监听页面加载完成事件,确保在页面完全加载后再执行JS。 - 将WKWebView添加到视图层级,保证页面能正常加载。
- 直接执行获取完整HTML的JS脚本(
document.documentElement.outerHTML),而非注册window.onload事件。 - 增加页面加载失败的处理逻辑,避免信号量一直等待导致主线程阻塞。
- 移除强制解包,增加兜底逻辑,避免崩溃。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

