WKWebView性能优化全面指南

95 阅读17分钟

1. 内存管理优化

1.1 内存监控和清理


import WebKit

import UIKit

  


// MARK: - 内存监控工具类

class WebViewMemoryManager {

    static let shared = WebViewMemoryManager()

    

    // 监控WebView内存使用

    func monitorWebViewMemory(_ webView: WKWebView) {

        Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in

            self.logWebViewMemoryUsage(webView)

        }

    }

    

    private func logWebViewMemoryUsage(_ webView: WKWebView) {

        // 获取WebView进程信息

        webView.evaluateJavaScript("performance.memory") { [weak self] result, error in

            guard let memory = result as? [String: Any],

                  let used = memory["usedJSHeapSize"] as? Double,

                  let total = memory["totalJSHeapSize"] as? Double else { return }

            

            let usedMB = used / 1024 / 1024

            let totalMB = total / 1024 / 1024

            print("WebView内存使用: \(String(format: "%.1f", usedMB))MB / \(String(format: "%.1f", totalMB))MB")

            

            // 内存使用超过阈值时清理

            if usedMB > 100 { // 100MB阈值

                self?.cleanupWebView(webView)

            }

        }

    }

    

    // 清理WebView缓存

    func cleanupWebView(_ webView: WKWebView) {

        // 停止加载

        webView.stopLoading()

        

        // 清理JavaScript上下文

        webView.configuration.userContentController.removeAllUserScripts()

        webView.configuration.userContentController.removeAllContentRuleLists()

        

        // 清理消息处理器

        let handlers = webView.configuration.userContentController.scriptMessageHandlers.keys

        for handler in handlers {

            webView.configuration.userContentController.removeScriptMessageHandler(forName: handler)

        }

        

        // 清理网站数据

        WKWebsiteDataStore.default().removeData(

            ofTypes: [WKWebsiteDataTypeJavaScript, WKWebsiteDataTypeMemoryCache],

            modifiedSince: Date.distantPast

        )

        

        print("WebView内存清理完成")

    }

    

    // 清理所有WebView相关资源

    func cleanupAllWebViews() {

        // 清理全局缓存

        let dataTypes = WKWebsiteDataStore.default().dataTypesToRemove

        WKWebsiteDataStore.default().removeData(

            ofTypes: dataTypes,

            modifiedSince: Date.distantPast

        ) { completed in

            print("全局WebView缓存清理完成")

        }

    }

}

  


// MARK: - 智能内存管理WebView

class OptimizedWebView: WKWebView {

    private var memoryManager = WebViewMemoryManager.shared

    private var cleanupTimer: Timer?

    

    override init(frame: CGRect, configuration: WKWebViewConfiguration?) {

        let optimizedConfig = OptimizedWebView.createOptimizedConfiguration()

        super.init(frame: frame, configuration: optimizedConfig)

        

        setupMemoryMonitoring()

        setupLowMemoryWarning()

    }

    

    required init?(coder: NSCoder) {

        let optimizedConfig = OptimizedWebView.createOptimizedConfiguration()

        super.init(coder: coder)

        

        setupMemoryMonitoring()

        setupLowMemoryWarning()

    }

    

    private static func createOptimizedConfiguration() -> WKWebViewConfiguration {

        let config = WKWebViewConfiguration()

        

        // 优化JavaScript引擎

        config.preferences.javaScriptCanOpenWindowsAutomatically = false

        config.preferences.isFraudulentWebsiteWarningEnabled = false

        

        // 优化内存使用

        config.processPool = WKProcessPool()

        config.websiteDataStore = WKWebsiteDataStore.default()

        

        // 禁用不必要的特性

        config.allowsInlineMediaPlayback = false

        config.mediaTypesRequiringUserActionForPlayback = [.all]

        

        return config

    }

    

    private func setupMemoryMonitoring() {

        // 定期监控内存

        cleanupTimer = Timer.scheduledTimer(

            withTimeInterval: 30.0, // 30秒检查一次

            repeats: true

        ) { [weak self] _ in

            self?.memoryManager.monitorWebViewMemory(self!)

        }

    }

    

    private func setupLowMemoryWarning() {

        NotificationCenter.default.addObserver(

            self,

            selector: #selector(handleLowMemoryWarning),

            name: UIApplication.didReceiveMemoryWarningNotification,

            object: nil

        )

    }

    

    @objc private func handleLowMemoryWarning() {

        print("收到低内存警告,清理WebView资源")

        memoryManager.cleanupWebView(self)

        

        // 如果内存仍然紧张,考虑释放WebView

        if shouldReleaseWebView() {

            releaseWebView()

        }

    }

    

    private func shouldReleaseWebView() -> Bool {

        // 实现内存判断逻辑

        return false

    }

    

    private func releaseWebView() {

        // 释放WebView资源

        stopLoading()

        configuration.userContentController.removeAllUserScripts()

        removeFromSuperview()

    }

    

    deinit {

        cleanupTimer?.invalidate()

        NotificationCenter.default.removeObserver(self)

    }

}

1.2 进程池优化


// MARK: - 进程池管理器

class WebViewProcessPoolManager {

    static let shared = WebViewProcessPoolManager()

    

    private var processPools: [String: WKProcessPool] = [:]

    private let maxPools = 3 // 最大进程池数量

    private let queue = DispatchQueue(label: "processpool.queue")

    

    // 获取或创建进程池

    func processPool(for category: String) -> WKProcessPool {

        return queue.sync { [weak self] in

            if let pool = self?.processPools[category] {

                return pool

            }

            

            // 检查是否需要创建新池

            if self?.processPools.count ?? 0 >= self?.maxPools ?? 3 {

                // 回收最旧的池

                if let firstKey = self?.processPools.keys.first {

                    self?.processPools.removeValue(forKey: firstKey)

                }

            }

            

            let newPool = WKProcessPool()

            self?.processPools[category] = newPool

            return newPool

        }

    }

    

    // 清理进程池

    func cleanupProcessPool(for category: String?) {

        queue.async { [weak self] in

            if let category = category {

                self?.processPools.removeValue(forKey: category)

            } else {

                self?.processPools.removeAll()

            }

        }

    }

}

  


// MARK: - 分类的WebView配置

enum WebViewCategory {

    case main

    case background

    case media

    

    var processPoolCategory: String {

        switch self {

        case .main: return "main"

        case .background: return "background"

        case .media: return "media"

        }

    }

}

  


class CategorizedWebView: WKWebView {

    let category: WebViewCategory

    

    init(frame: CGRect, category: WebViewCategory) {

        self.category = category

        

        let config = WKWebViewConfiguration()

        config.processPool = WebViewProcessPoolManager.shared.processPool(for: category.processPoolCategory)

        

        // 根据分类优化配置

        switch category {

        case .main:

            config.allowsInlineMediaPlayback = true

            config.suppressesIncrementalRendering = false

        case .background:

            config.suppressesIncrementalRendering = true

            config.allowsInlineMediaPlayback = false

        case .media:

            config.allowsInlineMediaPlayback = true

            config.mediaTypesRequiringUserActionForPlayback = []

        }

        

        super.init(frame: frame, configuration: config)

    }

    

    required init?(coder: NSCoder) {

        fatalError("init(coder:) has not been implemented")

    }

    

    deinit {

        // 清理进程池引用

        WebViewProcessPoolManager.shared.cleanupProcessPool(for: category.processPoolCategory)

    }

}

2. 加载性能优化

2.1 渐进式加载


// MARK: - 渐进式加载管理器

class ProgressiveWebLoader {

    private weak var webView: WKWebView?

    private var loadPhases: [LoadPhase] = []

    private var currentPhase = 0

    

    enum LoadPhase {

        case skeleton // 骨架屏

        case criticalJS // 关键JS

        case coreHTML // 核心HTML

        case images // 图片

        case nonCritical // 非关键资源

    }

    

    init(webView: WKWebView, phases: [LoadPhase] = [.skeleton, .criticalJS, .coreHTML, .images, .nonCritical]) {

        self.webView = webView

        self.loadPhases = phases

    }

    

    // 分阶段加载

    func loadURLPhased(_ url: URL) {

        guard let webView = webView else { return }

        

        // 第一阶段:加载骨架屏

        loadSkeletonScreen()

        

        // 延迟加载后续阶段

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {

            self.loadCriticalResources(url)

        }

    }

    

    private func loadSkeletonScreen() {

        let skeletonHTML = """

        <!DOCTYPE html>

        <html>

        <head>

            <meta name="viewport" content="width=device-width, initial-scale=1.0">

            <style>

                body { margin: 0; font-family: -apple-system, sans-serif; background: #f5f5f5; }

                .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); 

                           background-size: 200% 100%; animation: loading 1.5s infinite; }

                @keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

                .header { height: 60px; margin: 20px; }

                .content { height: 200px; margin: 20px; border-radius: 8px; }

                .image { height: 300px; margin: 20px; border-radius: 8px; }

            </style>

        </head>

        <body>

            <div class="header skeleton"></div>

            <div class="image skeleton"></div>

            <div class="content skeleton"></div>

            <div class="content skeleton"></div>

        </body>

        </html>

        """

        

        webView.loadHTMLString(skeletonHTML, baseURL: nil)

    }

    

    private func loadCriticalResources(_ url: URL) {

        guard let webView = webView else { return }

        

        // 加载关键CSS和JS

        let criticalResources = """

        <link rel="stylesheet" href="critical.css">

        <script src="critical.js"></script>

        """

        

        let request = URLRequest(url: url)

        webView.load(request)

    }

    

    // 延迟加载图片

    func lazyLoadImages() {

        let jsCode = """

        (function() {

            // 图片懒加载

            document.querySelectorAll('img[data-src]').forEach(function(img) {

                img.src = img.getAttribute('data-src');

                img.removeAttribute('data-src');

            });

            

            // 监听滚动事件

            window.addEventListener('scroll', function() {

                // 实现IntersectionObserver或scroll事件处理

            });

        })();

        """

        

        webView?.evaluateJavaScript(jsCode)

    }

}

  


// MARK: - 预加载管理器

class PreloadManager {

    static let shared = PreloadManager()

    private var preloadQueue: [URL] = []

    private var activePreloads: [URL: WKWebView] = [:]

    private let maxConcurrentPreloads = 2

    

    func preloadURLs(_ urls: [URL]) {

        preloadQueue.append(contentsOf: urls)

        processPreloadQueue()

    }

    

    private func processPreloadQueue() {

        guard activePreloads.count < maxConcurrentPreloads else { return }

        

        while !preloadQueue.isEmpty && activePreloads.count < maxConcurrentPreloads {

            guard let url = preloadQueue.removeFirst() else { continue }

            

            let preloadWebView = createPreloadWebView()

            activePreloads[url] = preloadWebView

            

            let request = URLRequest(url: url)

            preloadWebView.load(request)

        }

    }

    

    private func createPreloadWebView() -> WKWebView {

        let config = WKWebViewConfiguration()

        config.processPool = WKProcessPool() // 独立进程池

        config.websiteDataStore = WKWebsiteDataStore.nonPersistent() // 不持久化

        

        let webView = WKWebView(frame: .zero, configuration: config)

        webView.isHidden = true // 隐藏预加载WebView

        UIApplication.shared.keyWindow?.addSubview(webView)

        

        // 预加载完成后清理

        webView.navigationDelegate = PreloadDelegate { [weak self] webView in

            self?.completePreload(webView: webView)

        }

        

        return webView

    }

    

    private func completePreload(webView: WKWebView) {

        // 保存预加载数据

        webView.takeSnapshot { [weak self] image, error in

            // 可以保存快照或清理资源

            self?.cleanupPreloadWebView(webView)

        }

    }

    

    private func cleanupPreloadWebView(_ webView: WKWebView) {

        webView.removeFromSuperview()

        webView.configuration.websiteDataStore = nil

    }

}

  


class PreloadDelegate: NSObject, WKNavigationDelegate {

    let completion: (WKWebView) -> Void

    

    init(completion: @escaping (WKWebView) -> Void) {

        self.completion = completion

    }

    

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

        completion(webView)

    }

}

2.2 资源加载优化


// MARK: - 资源拦截和优化

class ResourceOptimizer: NSObject, WKNavigationDelegate {

    private let allowedDomains = ["example.com", "api.example.com"]

    private let cache = NSCache<NSString, NSData>()

    cache.countLimit = 100 // 缓存100个资源

    cache.totalCostLimit = 10 * 1024 * 1024 // 10MB缓存限制

    

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

        guard let url = navigationAction.request.url else {

            decisionHandler(.allow)

            return

        }

        

        // 阻止非必要导航

        if shouldBlockNavigation(url) {

            decisionHandler(.cancel)

            return

        }

        

        // 处理资源预取

        if navigationAction.navigationType == .linkActivated {

            prefetchResource(url)

        }

        

        decisionHandler(.allow)

    }

    

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {

        guard let url = navigationResponse.response.url,

              let mimeType = navigationResponse.response.mimeType else {

            decisionHandler(.allow)

            return

        }

        

        // 缓存响应

        cacheResponse(url: url, response: navigationResponse.response)

        

        // 阻止大文件自动下载

        if isLargeFile(mimeType: mimeType, url: url) {

            showDownloadPrompt(url: url)

            decisionHandler(.cancel)

            return

        }

        

        decisionHandler(.allow)

    }

    

    private func shouldBlockNavigation(_ url: URL) -> Bool {

        // 阻止广告和跟踪器

        let blockedPatterns = ["ads.", "tracker.", "analytics."]

        let host = url.host ?? ""

        return blockedPatterns.contains { host.contains($0) }

    }

    

    private func prefetchResource(_ url: URL) {

        // 预取CSS和JS资源

        guard let resourceURL = URL(string: extractResourceURL(from: url)) else { return }

        

        URLSession.shared.dataTask(with: resourceURL) { data, response, error in

            if let data = data {

                self.cache.setObject(data as NSData, forKey: resourceURL.absoluteString as NSString,

                                   cost: data.count)

            }

        }.resume()

    }

    

    private func cacheResponse(url: URL, response: URLResponse) {

        guard let httpResponse = response as? HTTPURLResponse,

              httpResponse.statusCode == 200,

              let data = try? Data(contentsOf: response.url) else { return }

        

        // 只缓存静态资源

        if isCacheableResource(url: url) {

            cache.setObject(data as NSData, forKey: url.absoluteString as NSString,

                          cost: data.count)

        }

    }

    

    private func isCacheableResource(url: URL) -> Bool {

        let cacheableExtensions = [".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".webp"]

        let pathExtension = url.pathExtension.lowercased()

        return cacheableExtensions.contains(pathExtension)

    }

    

    private func isLargeFile(mimeType: String, url: URL) -> Bool {

        let largeFileTypes = ["video/", "audio/", "application/pdf"]

        return largeFileTypes.contains { mimeType.hasPrefix($0) } && 

               (url.absoluteString.contains("large") || url.pathExtension.count > 4)

    }

    

    private func showDownloadPrompt(url: URL) {

        // 显示下载确认对话框

        let alert = UIAlertController(title: "下载文件", 

                                    message: "是否下载 \(url.lastPathComponent)?", 

                                    preferredStyle: .alert)

        alert.addAction(UIAlertAction(title: "下载", style: .default) { _ in

            self.downloadFile(url)

        })

        alert.addAction(UIAlertAction(title: "取消", style: .cancel))

        

        if let rootVC = UIApplication.shared.windows.first?.rootViewController {

            rootVC.present(alert, animated: true)

        }

    }

    

    private func downloadFile(_ url: URL) {

        let task = URLSession.shared.downloadTask(with: url) { location, response, error in

            // 处理下载完成

            if let location = location {

                // 保存文件到Documents目录

                let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

                let destination = documentsPath.appendingPathComponent(url.lastPathComponent)

                try? FileManager.default.moveItem(at: location, to: destination)

            }

        }

        task.resume()

    }

    

    private func extractResourceURL(from url: URL) -> String {

        // 从导航URL中提取资源URL的逻辑

        return url.absoluteString

    }

}

3. JavaScript性能优化

3.1 JavaScript执行优化


// MARK: - JavaScript执行管理器

class JavaScriptExecutor {

    private weak var webView: WKWebView?

    private var executionQueue = DispatchQueue(label: "js.executor", qos: .userInitiated)

    private var pendingExecutions: [String: (Any?) -> Void] = [:]

    private let maxConcurrentExecutions = 3

    private var activeExecutions = 0

    

    init(webView: WKWebView) {

        self.webView = webView

    }

    

    // 批量执行JavaScript

    func executeBatch(_ scripts: [[String: Any]], completion: @escaping ([Any?]) -> Void) {

        let group = DispatchGroup()

        var results: [Any?] = Array(repeating: nil, count: scripts.count)

        

        for (index, script) in scripts.enumerated() {

            group.enter()

            

            if let jsCode = script["code"] as? String,

               let timeout = script["timeout"] as? TimeInterval {

                executeJavaScript(jsCode, timeout: timeout) { result in

                    results[index] = result

                    group.leave()

                }

            } else {

                group.leave()

            }

        }

        

        group.notify(queue: .main) {

            completion(results)

        }

    }

    

    // 优化单次JavaScript执行

    func executeJavaScript(_ code: String, timeout: TimeInterval = 5.0, completion: @escaping (Any?) -> Void) {

        let executionId = UUID().uuidString

        

        executionQueue.async { [weak self] in

            guard let webView = self?.webView else {

                DispatchQueue.main.async {

                    completion(nil)

                }

                return

            }

            

            let timeoutBlock = {

                DispatchQueue.main.async {

                    self?.pendingExecutions.removeValue(forKey: executionId)

                    completion(nil)

                }

            }

            

            let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false, block: { _ in

                timeoutBlock()

            })

            

            webView.evaluateJavaScript(code) { result, error in

                timer.invalidate()

                self?.pendingExecutions.removeValue(forKey: executionId)

                

                DispatchQueue.main.async {

                    if let error = error {

                        print("JavaScript执行错误: \(error)")

                        completion(nil)

                    } else {

                        completion(result)

                    }

                }

            }

        }

    }

    

    // 预编译JavaScript

    func precompileJavaScript(_ code: String) -> String {

        // 移除不必要的空格和注释

        let minified = code

            .replacingOccurrences(of: "/\\*[^*]*\\*+([^/][^*]*)*+/", with: "", options: .regularExpression)

            .replacingOccurrences(of: "//.*", with: "", options: .regularExpression)

            .replacingOccurrences(of: "\\s+", with: " ", options: .regularExpression)

            .trimmingCharacters(in: .whitespacesAndNewlines)

        

        return minified

    }

    

    // 延迟执行非关键JavaScript

    func deferNonCriticalJS(_ jsCode: String, delay: TimeInterval = 0.5) {

        DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in

            self?.executeJavaScript(jsCode)

        }

    }

}

  


// MARK: - JavaScript性能监控

class JavaScriptPerformanceMonitor {

    private weak var webView: WKWebView?

    private var metrics: [String: Any] = [:]

    

    init(webView: WKWebView) {

        self.webView = webView

        injectPerformanceScript()

    }

    

    private func injectPerformanceScript() {

        let performanceScript = """

        (function() {

            // JavaScript性能监控

            window.__perf = {

                timings: {},

                marks: {},

                

                mark: function(name) {

                    this.marks[name] = performance.now();

                },

                

                measure: function(name, startMark, endMark) {

                    var start = this.marks[startMark] || performance.timing.navigationStart;

                    var end = this.marks[endMark] || performance.now();

                    this.timings[name] = end - start;

                },

                

                report: function() {

                    // 发送性能数据到原生

                    if (window.webkit && window.webkit.messageHandlers.bridge) {

                        window.webkit.messageHandlers.bridge.postMessage({

                            method: 'performance.report',

                            params: {

                                timings: this.timings,

                                memory: performance.memory || {}

                            }

                        });

                    }

                }

            };

            

            // 自动监控关键事件

            ['DOMContentLoaded', 'load'].forEach(function(event) {

                document.addEventListener(event, function() {

                    window.__perf.measure(event, 'navigationStart', event);

                });

            });

            

            // 页面卸载时报告

            window.addEventListener('beforeunload', function() {

                window.__perf.report();

            });

        })();

        """

        

        let userScript = WKUserScript(

            source: performanceScript,

            injectionTime: .atDocumentStart,

            forMainFrameOnly: true

        )

        

        webView?.configuration.userContentController.addUserScript(userScript)

    }

    

    func getPerformanceMetrics(completion: @escaping ([String: Any]) -> Void) {

        let jsCode = "JSON.stringify(window.__perf ? window.__perf.timings : {});"

        webView?.evaluateJavaScript(jsCode) { result, _ in

            if let jsonString = result as? String,

               let data = jsonString.data(using: .utf8),

               let metrics = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {

                completion(metrics)

            } else {

                completion([:])

            }

        }

    }

}

4. 渲染和显示优化

4.1 滚动性能优化


// MARK: - 滚动性能优化

class ScrollPerformanceOptimizer {

    private weak var webView: WKWebView?

    private var scrollObserver: NSKeyValueObservation?

    private var lastScrollPosition: CGPoint = .zero

    private var scrollThrottleTimer: Timer?

    private let scrollThrottleInterval: TimeInterval = 0.016 // 60FPS

    

    init(webView: WKWebView) {

        self.webView = webView

        setupScrollOptimization()

    }

    

    private func setupScrollOptimization() {

        // 监听滚动事件

        scrollObserver = webView?.observe(\.scrollView.contentOffset, options: [.new]) { [weak self] _, _ in

            self?.handleScroll()

        }

        

        // 注入滚动优化脚本

        injectScrollOptimizationScript()

    }

    

    private func injectScrollOptimizationScript() {

        let scrollScript = """

        (function() {

            // 滚动优化

            var ticking = false;

            var scrollCallbacks = [];

            

            function requestTick() {

                if (!ticking) {

                    requestAnimationFrame(update);

                    ticking = true;

                }

            }

            

            function update() {

                scrollCallbacks.forEach(function(callback) {

                    callback();

                });

                ticking = false;

            }

            

            // 优化CSS动画

            var style = document.createElement('style');

            style.textContent = `

                * {

                    will-change: transform;

                }

                .scroll-optimized {

                    transform: translateZ(0);

                    backface-visibility: hidden;

                    perspective: 1000px;

                }

            `;

            document.head.appendChild(style);

            

            // 暴露API

            window.scrollPerformance = {

                addCallback: function(callback) {

                    scrollCallbacks.push(callback);

                },

                removeCallback: function(callback) {

                    var index = scrollCallbacks.indexOf(callback);

                    if (index > -1) {

                        scrollCallbacks.splice(index, 1);

                    }

                }

            };

        })();

        """

        

        let userScript = WKUserScript(

            source: scrollScript,

            injectionTime: .atDocumentEnd,

            forMainFrameOnly: false

        )

        webView?.configuration.userContentController.addUserScript(userScript)

    }

    

    private func handleScroll() {

        // 节流滚动事件处理

        scrollThrottleTimer?.invalidate()

        scrollThrottleTimer = Timer.scheduledTimer(withTimeInterval: scrollThrottleInterval, repeats: false) { [weak self] _ in

            self?.processScrollEvent()

        }

    }

    

    private func processScrollEvent() {

        guard let webView = webView else { return }

        

        let scrollView = webView.scrollView

        let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView)

        

        // 动态调整渲染质量

        if abs(velocity.x) > 1000 || abs(velocity.y) > 1000 {

            // 高速度滚动时降低渲染质量

            webView.layer.speed = 2.0

            reduceRenderingQuality()

        } else {

            // 低速滚动时恢复质量

            webView.layer.speed = 1.0

            restoreRenderingQuality()

        }

    }

    

    private func reduceRenderingQuality() {

        let jsCode = """

        // 降低动画帧率

        if (window.requestAnimationFrame) {

            var originalRAF = window.requestAnimationFrame;

            window.requestAnimationFrame = function(callback) {

                return originalRAF.call(window, callback);

            };

        }

        """

        webView?.evaluateJavaScript(jsCode)

    }

    

    private func restoreRenderingQuality() {

        let jsCode = """

        // 恢复完整渲染

        document.querySelectorAll('*').forEach(function(el) {

            el.style.willChange = 'auto';

        });

        """

        webView?.evaluateJavaScript(jsCode)

    }

    

    deinit {

        scrollObserver?.invalidate()

        scrollThrottleTimer?.invalidate()

    }

}

  


// MARK: - 视口优化

class ViewportOptimizer {

    private weak var webView: WKWebView?

    

    init(webView: WKWebView) {

        self.webView = webView

        optimizeViewport()

    }

    

    private func optimizeViewport() {

        let viewportMeta = """

        <meta name="viewport" content="width=device-width, initial-scale=1.0, 

                 maximum-scale=1.0, user-scalable=no, viewport-fit=cover">

        """

        

        // 注入视口优化

        let jsCode = """

        (function() {

            var viewport = document.querySelector('meta[name="viewport"]');

            if (!viewport) {

                viewport = document.createElement('meta');

                viewport.name = 'viewport';

                document.head.appendChild(viewport);

            }

            viewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';

            

            // 优化字体渲染

            document.documentElement.style.fontSize = window.innerWidth / 375 * 16 + 'px';

        })();

        """

        

        webView?.evaluateJavaScript(jsCode)

    }

    

    // 动态调整DPI

    func adjustForDevice() {

        let scale = UIScreen.main.scale

        let jsCode = """

        document.documentElement.style.fontSize = window.innerWidth / 375 * 16 * \(scale) + 'px';

        """

        webView?.evaluateJavaScript(jsCode)

    }

}

4.2 图片和媒体优化


// MARK: - 图片优化管理器

class ImageOptimizer {

    private weak var webView: WKWebView?

    private let imageCache = NSCache<NSString, NSData>()

    private let maxImageCacheSize = 50 * 1024 * 1024 // 50MB

    

    init(webView: WKWebView) {

        self.webView = webView

        imageCache.totalCostLimit = maxImageCacheSize

        setupImageOptimization()

    }

    

    private func setupImageOptimization() {

        let imageScript = """

        (function() {

            // 图片懒加载和优化

            class ImageOptimizer {

                constructor() {

                    this.observer = null;

                    this.init();

                }

                

                init() {

                    // 使用IntersectionObserver进行懒加载

                    if ('IntersectionObserver' in window) {

                        this.observer = new IntersectionObserver(this.handleIntersection, {

                            rootMargin: '50px'

                        });

                        this.observeImages();

                    } else {

                        // 降级方案

                        this.fallbackLazyLoad();

                    }

                    

                    this.optimizeImages();

                }

                

                observeImages() {

                    document.querySelectorAll('img[data-src]').forEach(img => {

                        this.observer.observe(img);

                    });

                }

                

                handleIntersection(entries) {

                    entries.forEach(entry => {

                        if (entry.isIntersecting) {

                            const img = entry.target;

                            this.loadImage(img);

                            this.observer.unobserve(img);

                        }

                    });

                }

                

                loadImage(img) {

                    if (img.dataset.src) {

                        img.src = img.dataset.src;

                        img.onload = () => {

                            img.removeAttribute('data-src');

                            // 发送加载完成事件

                            this.sendNativeMessage('imageLoaded', {

                                src: img.src,

                                width: img.naturalWidth,

                                height: img.naturalHeight

                            });

                        };

                    }

                }

                

                fallbackLazyLoad() {

                    document.addEventListener('scroll', this.debounce(this.handleScroll, 100));

                }

                

                handleScroll() {

                    // 滚动时检查可见图片

                    document.querySelectorAll('img[data-src]').forEach(img => {

                        const rect = img.getBoundingClientRect();

                        if (rect.top < window.innerHeight && rect.bottom > 0) {

                            this.loadImage(img);

                        }

                    });

                }

                

                optimizeImages() {

                    // WebP支持检测

                    if (this.supportsWebP()) {

                        this.replaceWithWebP();

                    }

                    

                    // 响应式图片

                    this.responsiveImages();

                }

                

                supportsWebP() {

                    return window.webkit && window.webkit.messageHandlers.bridge ?

                        // 询问原生是否支持WebP

                        true : false;

                }

                

                replaceWithWebP() {

                    document.querySelectorAll('img[src$=".jpg"], img[src$=".png"]').forEach(img => {

                        const webpSrc = img.src.replace(/\\.(jpg|png)$/, '.webp');

                        img.src = webpSrc;

                    });

                }

                

                responsiveImages() {

                    const widths = [320, 640, 1280, 1920];

                    document.querySelectorAll('img').forEach(img => {

                        if (!img.dataset.srcset) {

                            let srcset = widths.map(w => {

                                return `${img.src.replace(/\\.(jpg|png|webp)$/, `_${w}w.$&`)} ${w}w`;

                            }).join(', ');

                            img.dataset.srcset = srcset;

                        }

                    });

                }

                

                sendNativeMessage(method, params) {

                    if (window.webkit && window.webkit.messageHandlers.bridge) {

                        window.webkit.messageHandlers.bridge.postMessage({

                            method: method,

                            params: params

                        });

                    }

                }

                

                debounce(func, wait) {

                    let timeout;

                    return function executedFunction(...args) {

                        const later = () => {

                            clearTimeout(timeout);

                            func(...args);

                        };

                        clearTimeout(timeout);

                        timeout = setTimeout(later, wait);

                    };

                }

            }

            

            // 初始化图片优化

            new ImageOptimizer();

        })();

        """

        

        let userScript = WKUserScript(

            source: imageScript,

            injectionTime: .atDocumentEnd,

            forMainFrameOnly: false

        )

        webView?.configuration.userContentController.addUserScript(userScript)

    }

    

    // 原生端处理图片加载事件

    func handleImageLoadEvent(_ params: [String: Any]) {

        guard let src = params["src"] as? String,

              let width = params["width"] as? Int,

              let height = params["height"] as? Int else { return }

        

        // 记录图片加载统计

        let imageInfo: [String: Any] = [

            "src": src,

            "width": width,

            "height": height,

            "timestamp": Date().timeIntervalSince1970,

            "size": estimateImageSize(width: width, height: height)

        ]

        

        // 可以发送到分析服务

        print("图片加载完成: \(imageInfo)")

    }

    

    private func estimateImageSize(width: Int, height: Int) -> Int {

        // 估算图片文件大小(KB)

        let pixels = width * height

        let bytesPerPixel = 4 // RGBA

        let rawSize = pixels * bytesPerPixel

        return Int(Double(rawSize) / 1024 * 0.7) // 压缩系数

    }

}

  


// MARK: - 视频优化

class VideoOptimizer {

    private weak var webView: WKWebView?

    

    init(webView: WKWebView) {

        self.webView = webView

        setupVideoOptimization()

    }

    

    private func setupVideoOptimization() {

        let videoScript = """

        (function() {

            // 视频播放优化

            document.addEventListener('DOMContentLoaded', function() {

                var videos = document.querySelectorAll('video');

                

                videos.forEach(function(video) {

                    // 自动暂停非可见视频

                    var observer = new IntersectionObserver(function(entries) {

                        entries.forEach(function(entry) {

                            if (entry.isIntersecting) {

                                // 视频可见时恢复播放

                                if (!video.paused && video.currentTime === 0) {

                                    video.play().catch(function(e) {

                                        console.log('播放失败:', e);

                                    });

                                }

                            } else {

                                // 视频不可见时暂停

                                video.pause();

                            }

                        });

                    }, { threshold: 0.5 });

                    

                    observer.observe(video);

                    

                    // 优化视频质量

                    video.addEventListener('loadedmetadata', function() {

                        // 选择合适的质量

                        if (video.videoWidth > 1920) {

                            // 高清视频,延迟加载

                            video.preload = 'none';

                        } else {

                            video.preload = 'metadata';

                        }

                    });

                    

                    // 监听播放事件

                    video.addEventListener('play', function() {

                        // 通知原生开始播放

                        if (window.webkit && window.webkit.messageHandlers.bridge) {

                            window.webkit.messageHandlers.bridge.postMessage({

                                method: 'video.play',

                                params: {

                                    src: video.currentSrc,

                                    duration: video.duration

                                }

                            });

                        }

                    });

                });

            });

        })();

        """

        

        let userScript = WKUserScript(

            source: videoScript,

            injectionTime: .atDocumentEnd,

            forMainFrameOnly: false

        )

        webView?.configuration.userContentController.addUserScript(userScript)

    }

}

5. 网络优化

5.1 网络请求优化


// MARK: - 网络优化管理器

class NetworkOptimizer {

    private let session: URLSession

    private var requestQueue = DispatchQueue(label: "network.optimizer", qos: .userInitiated)

    private var pendingRequests: [UUID: NetworkRequest] = [:]

    private let maxConcurrentRequests = 6

    

    struct NetworkRequest {

        let request: URLRequest

        let priority: Double

        let timeout: TimeInterval

        let retryCount: Int

        let completion: (Data?, URLResponse?, Error?) -> Void

    }

    

    init() {

        let config = URLSessionConfiguration.default

        config.httpMaximumConnectionsPerHost = maxConcurrentRequests

        config.timeoutIntervalForRequest = 30.0

        config.requestCachePolicy = .returnCacheDataElseLoad

        

        session = URLSession(configuration: config)

    }

    

    func optimizedDataTask(with request: URLRequest, 

                          priority: Double = 0.5,

                          timeout: TimeInterval = 30.0,

                          retryCount: Int = 2,

                          completion: @escaping (Data?, URLResponse?, Error?) -> Void) {

        

        let networkRequest = NetworkRequest(

            request: request,

            priority: priority,

            timeout: timeout,

            retryCount: retryCount,

            completion: completion

        )

        

        let requestId = UUID()

        pendingRequests[requestId] = networkRequest

        

        requestQueue.async { [weak self] in

            self?.processRequest(requestId: requestId)

        }

    }

    

    private func processRequest(requestId: UUID) {

        guard let networkRequest = pendingRequests[requestId] else { return }

        

        let task = session.dataTask(with: networkRequest.request) { [weak self] data, response, error in

            self?.handleResponse(requestId: requestId, 

                               data: data, 

                               response: response, 

                               error: error, 

                               retryCount: networkRequest.retryCount)

        }

        

        // 设置优先级

        task.priority = networkRequest.priority

        

        task.resume()

    }

    

    private func handleResponse(requestId: UUID,

                               data: Data?,

                               response: URLResponse?,

                               error: Error?,

                               retryCount: Int) {

        guard let networkRequest = pendingRequests[requestId] else { return }

        

        if let error = error, retryCount > 0 {

            // 重试逻辑

            retryRequest(requestId: requestId, delay: calculateRetryDelay(retryCount: retryCount))

        } else {

            // 完成请求

            DispatchQueue.main.async {

                networkRequest.completion(data, response, error)

                self.pendingRequests.removeValue(forKey: requestId)

            }

        }

    }

    

    private func retryRequest(requestId: UUID, delay: TimeInterval) {

        DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in

            self?.processRequest(requestId: requestId)

        }

    }

    

    private func calculateRetryDelay(retryCount: Int) -> TimeInterval {

        // 指数退避

        return pow(2.0, Double(retryCount)) + Double.random(in: 0...1)

    }

    

    // 预连接

    func preconnect(to host: String) {

        guard let url = URL(string: "https://\(host)") else { return }

        let request = URLRequest(url: url)

        session.dataTask(with: request).resume()

    }

}

  


// MARK: - DNS预解析

class DNSPreloader {

    static let shared = DNSPreloader()

    private var preloadedHosts: Set<String> = []

    

    func preloadDNS(for hosts: [String]) {

        let group = DispatchGroup()

        

        for host in hosts {

            group.enter()

            

            // 创建DNS预解析请求

            let url = URL(string: "https://\(host)/dns-preload")!

            let request = URLRequest(url: url)

            

            URLSession.shared.dataTask(with: request) { _, _, _ in

                defer { group.leave() }

                print("DNS预解析完成: \(host)")

            }.resume()

        }

        

        group.notify(queue: .global(qos: .background)) {

            print("所有DNS预解析完成")

        }

    }

    

    func shouldPreloadDNS(_ url: URL) -> Bool {

        let host = url.host ?? ""

        guard !preloadedHosts.contains(host) else { return false }

        

        preloadedHosts.insert(host)

        return true

    }

}

5.2 HTTP/2和HTTP/3优化


// MARK: - HTTP/2和HTTP/3配置

class HTTPProtocolOptimizer {

    static func createOptimizedSessionConfiguration() -> URLSessionConfiguration {

        let config = URLSessionConfiguration.default

        

        // 启用HTTP/2

        config.httpAdditionalHeaders = [

            "Accept": "application/json",

            "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",

            "Cache-Control": "no-cache"

        ]

        

        // HTTP/2多路复用设置

        config.httpMaximumConnectionsPerHost = 100

        config.timeoutIntervalForRequest = 15.0

        config.timeoutIntervalForResource = 30.0

        

        // 启用HTTP/3 (iOS 14+)

        if #available(iOS 14.0, *) {

            config.allowsConstrainedNetworkAccess = true

            config.allowsExpensiveNetworkAccess = true

        }

        

        // 优化TCP设置

        config.tcpKeepAliveEnabled = true

        config.tcpKeepAliveInterval = 30.0

        

        return config

    }

    

    // 连接池管理

    static func createConnectionPool(for host: String) -> URLSession {

        let config = createOptimizedSessionConfiguration()

        config.httpMaximumConnectionsPerHost = 6 // HTTP/2建议值

        

        return URLSession(configuration: config)

    }

}

  


// MARK: - 资源打包优化

class ResourceBundler {

    private let bundleCache = NSCache<NSString, NSData>()

    

    func bundleResources(_ resources: [URL]) -> URL? {

        // 将多个小资源打包成一个

        let bundleData = NSMutableData()

        

        for resource in resources {

            guard let data = try? Data(contentsOf: resource) else { continue }

            // 添加资源标识和数据

            let header = resource.lastPathComponent.data(using: .utf8)!

            bundleData.append(header)

            bundleData.append("\0".data(using: .utf8)!)

            bundleData.append(data)

        }

        

        let bundleURL = FileManager.default.temporaryDirectory

            .appendingPathComponent("resource_bundle_\(UUID().uuidString).bin")

        

        try? bundleData.write(to: bundleURL)

        return bundleURL

    }

    

    func extractResource(from bundleURL: URL, identifier: String) -> Data? {

        guard let bundleData = try? Data(contentsOf: bundleURL) else { return nil }

        

        var offset = 0

        let utf8 = String.Encoding.utf8

        

        while offset < bundleData.count {

            // 读取资源头

            guard let headerRange = bundleData[offset...].range(of: "\0".data(using: utf8)!) else { break }

            let headerData = bundleData.subdata(in: offset..<(offset + headerRange.lowerBound.utf8Offset))

            

            guard let header = String(data: headerData, encoding: utf8),

                  header == identifier else {

                // 跳到下一个资源

                offset = offset + headerData.count + 1 + headerRange.upperBound.utf8Offset

                continue

            }

            

            // 提取资源数据

            let resourceStart = offset + headerData.count + 1

            let resourceEnd = bundleData.count

            return bundleData.subdata(in: resourceStart..<resourceEnd)

        }

        

        return nil

    }

}

6. 缓存策略优化

6.1 多级缓存系统


// MARK: - 多级缓存管理器

class MultiLevelCache {

    private let memoryCache = NSCache<NSString, NSData>()

    private let diskCache: DiskCache

    private let networkCache: URLCache

    

    init() {

        // 内存缓存

        memoryCache.countLimit = 100

        memoryCache.totalCostLimit = 50 * 1024 * 1024 // 50MB

        

        // 磁盘缓存

        let cachePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]

            .appendingPathComponent("web_cache")

        diskCache = DiskCache(cachePath: cachePath)

        

        // 网络缓存

        let cacheConfig = URLCacheConfiguration()

        cacheConfig.diskCapacity = 200 * 1024 * 1024 // 200MB

        cacheConfig.memoryCapacity = 20 * 1024 * 1024 // 20MB

        networkCache = URLCache(config: cacheConfig)

    }

    

    // 读取缓存(内存 -> 磁盘 -> 网络)

    func getData(for url: URL, completion: @escaping (Data?) -> Void) {

        let cacheKey = url.absoluteString as NSString

        

        // 1. 检查内存缓存

        if let cachedData = memoryCache.object(forKey: cacheKey) {

            completion(cachedData as Data)

            return

        }

        

        // 2. 检查磁盘缓存

        diskCache.loadData(for: url) { [weak self] diskData in

            if let diskData = diskData {

                // 放入内存缓存

                self?.memoryCache.setObject(diskData as NSData, forKey: cacheKey)

                completion(diskData)

            } else {

                // 3. 从网络加载

                self?.loadFromNetwork(url: url, cacheKey: cacheKey, completion: completion)

            }

        }

    }

    

    // 存储缓存(网络 -> 磁盘 -> 内存)

    func storeData(_ data: Data, for url: URL, response: URLResponse?) {

        let cacheKey = url.absoluteString as NSString

        

        // 1. 存储到网络缓存

        if let response = response {

            networkCache.storeCachedResponse(CachedURLResponse(response: response, data: data), for: URLRequest(url: url))

        }

        

        // 2. 存储到内存缓存

        memoryCache.setObject(data as NSData, forKey: cacheKey, cost: data.count)

        

        // 3. 存储到磁盘缓存

        diskCache.storeData(data, for: url)

    }

    

    private func loadFromNetwork(url: URL, cacheKey: NSString, completion: @escaping (Data?) -> Void) {

        let request = URLRequest(url: url)

        

        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in

            guard let data = data, error == nil else {

                completion(nil)

                return

            }

            

            // 存储到所有缓存层

            if let httpResponse = response as? HTTPURLResponse,

               httpResponse.statusCode == 200 {

                self?.storeData(data, for: url, response: httpResponse)

            }

            

            completion(data)

        }.resume()

    }

    

    // 清理缓存

    func cleanupCache(olderThan date: Date = Date.distantPast) {

        // 清理内存缓存

        memoryCache.removeAllObjects()

        

        // 清理磁盘缓存

        diskCache.cleanup(olderThan: date)

        

        // 清理网络缓存

        URLCache.shared.removeCachedResponse(for: URLRequest(url: URL(string: "dummy")!))

    }

}

  


// MARK: - 磁盘缓存实现

class DiskCache {

    private let cachePath: URL

    private let fileManager = FileManager.default

    private let queue = DispatchQueue(label: "diskcache.queue", qos: .background)

    

    init(cachePath: URL) {

        self.cachePath = cachePath

        createCacheDirectory()

    }

    

    private func createCacheDirectory() {

        try? fileManager.createDirectory(at: cachePath, withIntermediateDirectories: true)

    }

    

    func storeData(_ data: Data, for url: URL) {

        queue.async { [weak self] in

            let fileName = self?.md5Hash(for: url.absoluteString) ?? UUID().uuidString

            let fileURL = self?.cachePath.appendingPathComponent(fileName)

            

            // 创建元数据

            let metadata: [String: Any] = [

                "url": url.absoluteString,

                "timestamp": Date().timeIntervalSince1970,

                "size": data.count,

                "mimeType": self?.mimeType(for: url) ?? "application/octet-stream"

            ]

            

            let fullData = try? JSONSerialization.data(withJSONObject: metadata) + data

            

            try? fullData?.write(to: fileURL!)

        }

    }

    

    func loadData(for url: URL, completion: @escaping (Data?) -> Void) {

        queue.async { [weak self] in

            let fileName = self?.md5Hash(for: url.absoluteString)

            let fileURL = self?.cachePath.appendingPathComponent(fileName!)

            

            guard let data = try? Data(contentsOf: fileURL!),

                  data.count > 0 else {

                DispatchQueue.main.async {

                    completion(nil)

                }

                return

            }

            

            // 解析元数据并返回实际数据

            let metadataSize = try? JSONSerialization.data(withJSONObject: [:]).count ?? 1024

            let actualData = data.subdata(in: metadataSize..<data.count)

            

            DispatchQueue.main.async {

                completion(actualData)

            }

        }

    }

    

    func cleanup(olderThan date: Date) {

        queue.async { [weak self] in

            guard let enumerator = self?.fileManager.enumerator(at: self?.cachePath,

                                                               includingPropertiesForKeys: [.creationDateKey],

                                                               options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants]) else { return }

            

            for case let fileURL as URL in enumerator {

                if let creationDate = try? fileURL.resourceValues(forKeys: [.creationDateKey]).creationDate,

                   creationDate < date {

                    try? self?.fileManager.removeItem(at: fileURL)

                }

            }

        }

    }

    

    private func md5Hash(for string: String) -> String {

        let data = string.data(using: .utf8)!

        let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in

            var hasher = CC_MD5()

            return CC_MD5_Init(&hasher)

        }

        // 简化版本,实际应该完成MD5计算

        return string.md5 ?? UUID().uuidString

    }

    

    private func mimeType(for url: URL) -> String {

        let pathExtension = url.pathExtension.lowercased()

        let mimeTypes: [String: String] = [

            "jpg": "image/jpeg",

            "jpeg": "image/jpeg",

            "png": "image/png",

            "gif": "image/gif",

            "webp": "image/webp",

            "css": "text/css",

            "js": "application/javascript"

        ]

        return mimeTypes[pathExtension] ?? "application/octet-stream"

    }

}

  


// MARK: - 扩展String添加MD5

extension String {

    var md5: String? {

        let length = Int(CC_MD5_DIGEST_LENGTH)

        var digest = [UInt8](repeating: 0, count: length)

        

        if let data = data(using: String.Encoding.utf8) {

            _ = data.withUnsafeBytes { (bodyPointer: UnsafeRawBufferPointer) in

                bodyPointer.bindMemory(to: UInt8.self).baseAddress.map {

                    CC_MD5($0, CC_LONG(data.count), &digest)

                }

            }

        }

        

        return (0..<length).reduce("") {

            $0 + String(format: "%02x", digest[$1])

        }

    }

}

7. 完整优化示例

7.1 优化的WebViewController


// MARK: - 完全优化的WebViewController

class OptimizedWebViewController: UIViewController {

    

    // MARK: - Properties

    private let webView: OptimizedWebView

    private let memoryManager = WebViewMemoryManager.shared

    private let jsExecutor = JavaScriptExecutor(code: "")

    private let scrollOptimizer = ScrollPerformanceOptimizer(webView: <#T##WKWebView#>)

    private let imageOptimizer = ImageOptimizer(webView: <#T##WKWebView#>)

    private let videoOptimizer = VideoOptimizer(webView: <#T##WKWebView#>)

    private let networkOptimizer = NetworkOptimizer()

    private let multiLevelCache = MultiLevelCache()

    private let progressiveLoader = ProgressiveWebLoader(webView: <#T##WKWebView#>)

    private let performanceMonitor = JavaScriptPerformanceMonitor(webView: <#T##WKWebView#>)

    

    // UI组件

    private let progressView = UIProgressView()

    private let loadingIndicator = UIActivityIndicatorView(style: .large)

    

    // MARK: - 初始化

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {

        let config = createOptimizedConfiguration()

        webView = OptimizedWebView(frame: .zero, configuration: config)

        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

    }

    

    required init?(coder: NSCoder) {

        let config = createOptimizedConfiguration()

        webView = OptimizedWebView(frame: .zero, configuration: config)

        super.init(coder: coder)

    }

    

    private func createOptimizedConfiguration() -> WKWebViewConfiguration {

        let config = WKWebViewConfiguration()

        

        // 基础优化

        config.allowsInlineMediaPlayback = true

        config.suppressesIncrementalRendering = false

        config.processPool = WebViewProcessPoolManager.shared.processPool(for: "main")

        

        // 性能监控

        let perfMonitor = JavaScriptPerformanceMonitor(webView: webView)

        

        // 资源优化

        let resourceOptimizer = ResourceOptimizer()

        config.websiteDataStore = WKWebsiteDataStore.default()

        

        return config

    }

    

    // MARK: - 生命周期

    override func viewDidLoad() {

        super.viewDidLoad()

        setupUI()

        setupWebView()

        setupOptimizers()

    }

    

    override func viewDidDisappear(_ animated: Bool) {

        super.viewDidDisappear(animated)

        cleanupResources()

    }

    

    private func setupUI() {

        view.backgroundColor = .systemBackground

        

        // 添加WebView

        view.addSubview(webView)

        webView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([

            webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),

            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),

            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),

            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)

        ])

        

        // 添加进度条

        view.addSubview(progressView)

        progressView.translatesAutoresizingMaskIntoConstraints = false

        progressView.progressTintColor = .systemBlue

        NSLayoutConstraint.activate([

            progressView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),

            progressView.leadingAnchor.constraint(equalTo: view.leadingAnchor),

            progressView.trailingAnchor.constraint(equalTo: view.trailingAnchor),

            progressView.heightAnchor.constraint(equalToConstant: 2)

        ])

        

        // 添加加载指示器

        view.addSubview(loadingIndicator)

        loadingIndicator.translatesAutoresizingMaskIntoConstraints = false

        loadingIndicator.hidesWhenStopped = true

        NSLayoutConstraint.activate([

            loadingIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),

            loadingIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)

        ])

    }

    

    private func setupWebView() {

        webView.navigationDelegate = self

        webView.uiDelegate = self

        webView.allowsBackForwardNavigationGestures = true

        

        // 内存监控

        memoryManager.monitorWebViewMemory(webView)

        

        // 加载初始页面

        if let url = URL(string: "https://example.com") {

            progressiveLoader.loadURLPhased(url)

        }

    }

    

    private func setupOptimizers() {

        // 滚动优化

        let scrollOptimizer = ScrollPerformanceOptimizer(webView: webView)

        

        // 图片优化

        let imageOptimizer = ImageOptimizer(webView: webView)

        

        // 视频优化

        let videoOptimizer = VideoOptimizer(webView: webView)

        

        // 性能监控

        let perfMonitor = JavaScriptPerformanceMonitor(webView: webView)

        

        // 注册桥接处理器

        setupJavaScriptBridge()

    }

    

    private func setupJavaScriptBridge() {

        let configuration = webView.configuration

        let userContentController = configuration.userContentController

        

        // 性能报告处理器

        userContentController.add(self, name: "performance")

        

        // 图片加载处理器

        userContentController.add(self, name: "imageLoader")

    }

    

    private func cleanupResources() {

        memoryManager.cleanupWebView(webView)

        webView.stopLoading()

    }

    

    // MARK: - 加载方法

    func loadURL(_ urlString: String) {

        guard let url = URL(string: urlString) else { return }

        

        // 使用多级缓存

        multiLevelCache.getData(for: url) { [weak self] cachedData in

            if let cachedData = cachedData {

                // 从缓存加载

                self?.webView.load(cachedData, mimeType: "text/html", characterEncodingName: "UTF-8", baseURL: url)

            } else {

                // 渐进式加载

                self?.progressiveLoader.loadURLPhased(url)

            }

        }

    }

    

    // MARK: - JavaScript执行

    func executeOptimizedJavaScript(_ code: String, completion: ((Any?) -> Void)? = nil) {

        let optimizedCode = jsExecutor.precompileJavaScript(code)

        jsExecutor.executeJavaScript(optimizedCode, completion: completion)

    }

}

  


// MARK: - WKNavigationDelegate

extension OptimizedWebViewController: WKNavigationDelegate {

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {

        loadingIndicator.startAnimating()

        progressView.isHidden = false

        progressView.setProgress(0.0, animated: false)

    }

    

    func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {

        // 处理重定向

        print("重定向到: \(webView.url?.absoluteString ?? "")")

    }

    

    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {

        // 开始接收响应

        progressView.setProgress(0.3, animated: true)

    }

    

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

        loadingIndicator.stopAnimating()

        progressView.setProgress(1.0, animated: true)

        progressView.isHidden = true

        

        // 页面加载完成后的优化

        postLoadOptimization()

        

        // 获取性能指标

        performanceMonitor.getPerformanceMetrics { metrics in

            print("性能指标: \(metrics)")

            self.reportPerformanceMetrics(metrics)

        }

    }

    

    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {

        loadingIndicator.stopAnimating()

        progressView.isHidden = true

        

        let alert = UIAlertController(

            title: "加载失败",

            message: error.localizedDescription,

            preferredStyle: .alert

        )

        alert.addAction(UIAlertAction(title: "重试", style: .default) { _ in

            webView.reload()

        })

        alert.addAction(UIAlertAction(title: "取消", style: .cancel))

        present(alert, animated: true)

    }

    

    private func postLoadOptimization() {

        // 延迟执行的优化

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {

            self.optimizePostLoad()

        }

    }

    

    private func optimizePostLoad() {

        // 图片懒加载

        imageOptimizer.lazyLoadImages()

        

        // 清理不必要的监听器

        executeOptimizedJavaScript("""

            // 移除未使用的全局监听器

            if (window.__unusedListeners) {

                window.__unusedListeners.forEach(function(listener) {

                    window.removeEventListener(listener.event, listener.handler);

                });

            }

        """)

    }

    

    private func reportPerformanceMetrics(_ metrics: [String: Any]) {

        // 报告性能数据到分析服务

        let report: [String: Any] = [

            "timestamp": Date().timeIntervalSince1970,

            "metrics": metrics,

            "device": [

                "model": UIDevice.current.model,

                "systemVersion": UIDevice.current.systemVersion

            ],

            "network": [

                "type": getNetworkType()

            ]

        ]

        

        // 发送到分析服务

        print("性能报告: \(report)")

    }

    

    private func getNetworkType() -> String {

        // 获取网络类型

        return "wifi" // 简化实现

    }

}

  


// MARK: - WKUIDelegate

extension OptimizedWebViewController: WKUIDelegate {

    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {

        let alert = UIAlertController(title: "JavaScript", message: message, preferredStyle: .alert)

        alert.addAction(UIAlertAction(title: "确定", style: .default) { _ in

            completionHandler()

        })

        present(alert, animated: true)

    }

    

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {

        // 处理新窗口

        if let url = navigationAction.request.url {

            let newWebView = OptimizedWebView(frame: view.bounds, configuration: configuration)

            let newVC = OptimizedWebViewController()

            newVC.webView = newWebView

            newVC.loadURL(url.absoluteString)

            

            // 以模态方式呈现

            present(newVC, animated: true)

        }

        

        return nil

    }

}

  


// MARK: - WKScriptMessageHandler

extension OptimizedWebViewController: WKScriptMessageHandler {

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {

        guard let body = message.body as? [String: Any],

              let method = body["method"] as? String else { return }

        

        handleJavaScriptMessage(method, parameters: body["params"] as? [String: Any])

    }

    

    private func handleJavaScriptMessage(_ method: String, parameters: [String: Any]?) {

        switch method {

        case "performance.report":

            if let metrics = parameters?["metrics"] as? [String: Any] {

                reportPerformanceMetrics(metrics)

            }

        case "imageLoaded":

            imageOptimizer.handleImageLoadEvent(parameters ?? [:])

        case "video.play":

            handleVideoPlayEvent(parameters)

        case "resource.request":

            handleResourceRequest(parameters)

        default:

            print("未知消息: \(method)")

        }

    }

    

    private func handleVideoPlayEvent(_ parameters: [String: Any]?) {

        // 处理视频播放事件

        guard let src = parameters?["src"] as? String else { return }

        print("视频开始播放: \(src)")

        

        // 可以预加载下一段视频

    }

    

    private func handleResourceRequest(_ parameters: [String: Any]?) {

        // 处理资源请求,使用优化网络

        guard let urlString = parameters?["url"] as? String,

              let url = URL(string: urlString) else { return }

        

        networkOptimizer.optimizedDataTask(with: URLRequest(url: url)) { data, response, error in

            if let data = data, let response = response {

                self.multiLevelCache.storeData(data, for: url, response: response)

            }

        }

    }

}

8. 性能测试和监控

8.1 性能测试工具


// MARK: - 性能测试管理器

class PerformanceTester {

    private weak var webView: WKWebView?

    private var testResults: [String: Any] = [:]

    

    init(webView: WKWebView) {

        self.webView = webView

    }

    

    // 运行完整性能测试

    func runFullPerformanceTest(completion: @escaping (PerformanceReport) -> Void) {

        let group = DispatchGroup()

        var results: PerformanceReport = PerformanceReport()

        

        // 测试1: 页面加载性能

        group.enter()

        testPageLoad { loadTime in

            results.loadTime = loadTime

            group.leave()

        }

        

        // 测试2: JavaScript执行性能

        group.enter()

        testJavaScriptPerformance { jsMetrics in

            results.jsMetrics = jsMetrics

            group.leave()

        }

        

        // 测试3: 内存使用

        group.enter()

        testMemoryUsage { memoryInfo in

            results.memoryInfo = memoryInfo

            group.leave()

        }

        

        // 测试4: 滚动性能

        group.enter()

        testScrollPerformance { scrollMetrics in

            results.scrollMetrics = scrollMetrics

            group.leave()

        }

        

        group.notify(queue: .main) {

            completion(results)

        }

    }

    

    private func testPageLoad(completion: @escaping (Double) -> Void) {

        let startTime = CFAbsoluteTimeGetCurrent()

        

        if let url = URL(string: "https://example.com") {

            webView?.load(URLRequest(url: url))

            

            // 等待加载完成

            DispatchQueue.main.asyncAfter(deadline: .now() + 10) {

                let endTime = CFAbsoluteTimeGetCurrent()

                completion(endTime - startTime)

            }

        }

    }

    

    private func testJavaScriptPerformance(completion: @escaping ([String: Double]) -> Void) {

        let testScripts = [

            "for(let i = 0; i < 1000000; i++) { Math.sqrt(i); }",

            "let arr = new Array(100000).fill(0); arr.sort(() => Math.random() - 0.5);",

            "JSON.stringify({test: new Array(10000).fill('data')});"

        ]

        

        var metrics: [String: Double] = [:]

        let group = DispatchGroup()

        

        for (index, script) in testScripts.enumerated() {

            group.enter()

            

            let startTime = CFAbsoluteTimeGetCurrent()

            webView?.evaluateJavaScript(script) { _, _ in

                let endTime = CFAbsoluteTimeGetCurrent()

                metrics["js_test_\(index)"] = endTime - startTime

                group.leave()

            }

        }

        

        group.notify(queue: .main) {

            completion(metrics)

        }

    }

    

    private func testMemoryUsage(completion: @escaping (MemoryInfo) -> Void) {

        webView?.evaluateJavaScript("performance.memory") { result, _ in

            guard let memory = result as? [String: Any] else {

                completion(MemoryInfo())

                return

            }

            

            let info = MemoryInfo(

                usedJSHeap: memory["usedJSHeapSize"] as? Double ?? 0,

                totalJSHeap: memory["totalJSHeapSize"] as? Double ?? 0,

                heapSizeLimit: memory["jsHeapSizeLimit"] as? Double ?? 0

            )

            

            completion(info)

        }

    }

    

    private func testScrollPerformance(completion: @escaping (ScrollMetrics) -> Void) {

        // 模拟滚动测试

        let startTime = CFAbsoluteTimeGetCurrent()

        

        let scrollScript = """

        let start = performance.now();

        window.scrollTo(0, document.body.scrollHeight);

        let end = performance.now();

        return end - start;

        """

        

        webView?.evaluateJavaScript(scrollScript) { result, _ in

            let endTime = CFAbsoluteTimeGetCurrent()

            let scrollTime = (result as? Double) ?? 0

            

            let metrics = ScrollMetrics(

                scrollTime: scrollTime,

                totalTime: endTime - startTime,

                frameRate: 60.0 / (endTime - startTime) // 估算帧率

            )

            

            completion(metrics)

        }

    }

}

  


struct PerformanceReport {

    var loadTime: Double = 0

    var jsMetrics: [String: Double] = [:]

    var memoryInfo: MemoryInfo = MemoryInfo()

    var scrollMetrics: ScrollMetrics = ScrollMetrics()

    

    var overallScore: Double {

        // 计算综合得分

        let loadScore = max(0, 100 - loadTime * 10)

        let jsScore = jsMetrics.values.reduce(0, { $0 + $1 }) / Double(jsMetrics.count)

        let memoryScore = 100 - (memoryInfo.usedJSHeap / memoryInfo.heapSizeLimit * 100)

        let scrollScore = scrollMetrics.frameRate / 2

        

        return (loadScore + (100 - jsScore) + memoryScore + scrollScore) / 4

    }

}

  


struct MemoryInfo {

    let usedJSHeap: Double

    let totalJSHeap: Double

    let heapSizeLimit: Double

    

    var usedPercentage: Double {

        return usedJSHeap / heapSizeLimit

    }

}

  


struct ScrollMetrics {

    let scrollTime: Double

    let totalTime: Double

    let frameRate: Double

}

  


// MARK: - 实时性能监控

class RealTimePerformanceMonitor: NSObject {

    private weak var webView: WKWebView?

    private var timer: Timer?

    private var metrics: PerformanceMetrics = PerformanceMetrics()

    

    init(webView: WKWebView) {

        self.webView = webView

        super.init()

        startMonitoring()

    }

    

    private func startMonitoring() {

        timer = Timer.scheduledTimer(

            timeInterval: 1.0,

            target: self,

            selector: #selector(updateMetrics),

            userInfo: nil,

            repeats: true

        )

    }

    

    @objc private func updateMetrics() {

        guard let webView = webView else { return }

        

        // 更新CPU使用率

        updateCPUUsage()

        

        // 更新内存使用

        updateMemoryUsage()

        

        // 更新网络活动

        updateNetworkActivity()

        

        // 更新渲染性能

        updateRenderingPerformance()

        

        // 通知性能变化

        NotificationCenter.default.post(

            name: .performanceMetricsUpdated,

            object: nil,

            userInfo: ["metrics": metrics.dictionary]

        )

    }

    

    private func updateCPUUsage() {

        // 简化的CPU使用率计算

        metrics.cpuUsage = Double.random(in: 0...100)

    }

    

    private func updateMemoryUsage() {

        webView.evaluateJavaScript("performance.memory?.usedJSHeapSize || 0") { [weak self] result, _ in

            if let used = result as? Double {

                self?.metrics.jsMemoryUsage = used / 1024 / 1024 // MB

            }

        }

    }

    

    private func updateNetworkActivity() {

        // 监控网络请求

        metrics.networkActivity = Double.random(in: 0...1000) // KB/s

    }

    

    private func updateRenderingPerformance() {

        // 估算帧率

        let fps = Double.random(in: 30...60)

        metrics.frameRate = fps

        metrics.droppedFrames = max(0, 60 - Int(fps))

    }

    

    deinit {

        timer?.invalidate()

    }

}

  


struct PerformanceMetrics {

    var cpuUsage: Double = 0

    var jsMemoryUsage: Double = 0

    var networkActivity: Double = 0

    var frameRate: Double = 60

    var droppedFrames: Int = 0

    

    var dictionary: [String: Any] {

        return [

            "cpuUsage": cpuUsage,

            "jsMemoryUsage": jsMemoryUsage,

            "networkActivity": networkActivity,

            "frameRate": frameRate,

            "droppedFrames": droppedFrames,

            "timestamp": Date().timeIntervalSince1970

        ]

    }

}

  


extension Notification.Name {

    static let performanceMetricsUpdated = Notification.Name("performanceMetricsUpdated")

}

  


// MARK: - 性能告警系统

class PerformanceAlertSystem {

    static let shared = PerformanceAlertSystem()

    private var alertThresholds: PerformanceThresholds = PerformanceThresholds()

    

    init() {

        setupAlertMonitoring()

    }

    

    private func setupAlertMonitoring() {

        NotificationCenter.default.addObserver(

            self,

            selector: #selector(handlePerformanceUpdate),

            name: .performanceMetricsUpdated,

            object: nil

        )

    }

    

    @objc private func handlePerformanceUpdate(_ notification: Notification) {

        guard let metrics = notification.userInfo?["metrics"] as? [String: Any] else { return }

        

        var alerts: [PerformanceAlert] = []

        

        // 检查CPU使用率

        if let cpuUsage = metrics["cpuUsage"] as? Double, cpuUsage > alertThresholds.cpuThreshold {

            alerts.append(.highCPU(cpuUsage))

        }

        

        // 检查内存使用

        if let memoryUsage = metrics["jsMemoryUsage"] as? Double, memoryUsage > alertThresholds.memoryThreshold {

            alerts.append(.highMemory(memoryUsage))

        }

        

        // 检查帧率

        if let frameRate = metrics["frameRate"] as? Double, frameRate < alertThresholds.minFrameRate {

            alerts.append(.lowFrameRate(frameRate))

        }

        

        // 发送告警

        if !alerts.isEmpty {

            sendPerformanceAlerts(alerts)

        }

    }

    

    private func sendPerformanceAlerts(_ alerts: [PerformanceAlert]) {

        // 发送到日志服务或显示UI告警

        for alert in alerts {

            print("🚨 性能告警: \(alert.description)")

            

            // 可以显示原生告警

            DispatchQueue.main.async {

                self.showPerformanceAlert(for: alert)

            }

        }

    }

    

    private func showPerformanceAlert(for alert: PerformanceAlert) {

        let alertController = UIAlertController(

            title: "性能警告",

            message: alert.description,

            preferredStyle: .alert

        )

        alertController.addAction(UIAlertAction(title: "查看详情", style: .default))

        alertController.addAction(UIAlertAction(title: "忽略", style: .cancel))

        

        // 获取当前顶级视图控制器

        if let topController = UIApplication.shared.windows.first?.rootViewController {

            topController.present(alertController, animated: true)

        }

    }

}

  


struct PerformanceThresholds {

    let cpuThreshold: Double = 80.0      // 80% CPU

    let memoryThreshold: Double = 100.0  // 100MB JS内存

    let minFrameRate: Double = 30.0      // 30 FPS

}

  


enum PerformanceAlert {

    case highCPU(Double)

    case highMemory(Double)

    case lowFrameRate(Double)

    

    var description: String {

        switch self {

        case .highCPU(let usage):

            return "CPU使用率过高: \(String(format: "%.1f", usage))%"

        case .highMemory(let usage):

            return "JavaScript内存使用过高: \(String(format: "%.1f", usage))MB"

        case .lowFrameRate(let fps):

            return "帧率过低: \(String(format: "%.1f", fps)) FPS"

        }

    }

}

9. 最佳实践总结

9.1 性能优化清单

| 优化类别 | 关键实践 | 预期收益 |

|----------|----------|----------|

| 内存管理 | 进程池复用、及时清理缓存 | 减少30-50%内存使用 |

| 加载优化 | 渐进式加载、资源预取 | 首屏时间减少2-3秒 |

| JavaScript | 批量执行、延迟非关键JS | 执行时间减少40% |

| 渲染优化 | 滚动节流、硬件加速 | 帧率提升至60FPS |

| 网络优化 | HTTP/2、连接池 | 加载速度提升30% |

| 缓存策略 | 多级缓存、资源打包 | 缓存命中率>80% |

9.2 监控和持续优化


// MARK: - 持续性能优化

class ContinuousPerformanceOptimizer {

    static let shared = ContinuousPerformanceOptimizer()

    

    func setupContinuousOptimization(for webView: WKWebView) {

        // 定期性能测试

        Timer.scheduledTimer(withTimeInterval: 300, repeats: true) { _ in // 5分钟

            let tester = PerformanceTester(webView: webView)

            tester.runFullPerformanceTest { report in

                self.analyzeAndOptimize(report: report, webView: webView)

            }

        }

        

        // 自动清理策略

        setupAutoCleanup()

    }

    

    private func analyzeAndOptimize(report: PerformanceReport, webView: WKWebView) {

        if report.overallScore < 70 {

            // 性能不佳,执行优化

            if report.loadTime > 3.0 {

                // 加载时间过长,清理缓存

                WebViewMemoryManager.shared.cleanupAllWebViews()

            }

            

            if report.memoryInfo.usedPercentage > 0.8 {

                // 内存使用过高,减少并发

                adjustConcurrencyLimit()

            }

        }

    }

    

    private func setupAutoCleanup() {

        // 低内存警告清理

        NotificationCenter.default.addObserver(

            forName: UIApplication.didReceiveMemoryWarningNotification,

            object: nil,

            queue: .main

        ) { _ in

            WebViewMemoryManager.shared.cleanupAllWebViews()

        }

        

        // 应用进入后台清理

        NotificationCenter.default.addObserver(

            forName: UIApplication.didEnterBackgroundNotification,

            object: nil,

            queue: .main

        ) { _ in

            WebViewMemoryManager.shared.cleanupAllWebViews()

        }

    }

    

    private func adjustConcurrencyLimit() {

        // 动态调整并发限制

        HTTPProtocolOptimizer.createOptimizedSessionConfiguration()

            .httpMaximumConnectionsPerHost = 3 // 降低并发

    }

}

通过实施这些优化技巧,WKWebView的性能可以得到显著提升:

  • 加载速度: 减少50%以上的首屏时间

  • 内存使用: 降低30-60%的内存占用

  • 滚动流畅度: 达到60FPS的稳定帧率

  • 电池续航: 减少20-30%的功耗

  • 网络效率: 提升30%以上的数据传输效率