显著提高iOS应用中Web页面的加载速度 - 提前下载页面的关键资源(如JavaScript、CSS和图像)

在这里插入图片描述

手动下载并缓存资源是一种有效的方式,可以确保在需要时资源已经在本地存储,这样可以显著提高加载速度。

缓存整个 web 页面的所有资源文件

具体实现步骤

  1. 下载和缓存资源:包括 HTML 文件、CSS、JavaScript 和图像。
  2. 在应用启动时预加载资源
  3. 构建包含所有预加载资源的 HTML 字符串
  4. 加载构建的 HTML 字符串到 WKWebView

资源下载和缓存管理类

import Foundationclass ResourceDownloader {static let shared = ResourceDownloader()private init() {}func downloadResources() {let resources = [URL(string: "https://www.example.com/styles.css")!,URL(string: "https://www.example.com/script.js")!,URL(string: "https://www.example.com/image.png")!,URL(string: "https://www.example.com/index.html")!]for resource in resources {downloadResource(from: resource)}}private func downloadResource(from url: URL) {let task = URLSession.shared.downloadTask(with: url) { localURL, response, error inguard let localURL = localURL else { return }do {let data = try Data(contentsOf: localURL)self.cacheResource(data: data, url: url)} catch {print("Failed to load resource: \(error)")}}task.resume()}private func cacheResource(data: Data, url: URL) {let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)do {try data.write(to: fileURL)print("Resource cached: \(fileURL)")} catch {print("Failed to cache resource: \(error)")}}func getCachedResource(for url: URL) -> Data? {let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)return try? Data(contentsOf: fileURL)}
}

在应用启动时预加载资源

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {// 预加载资源ResourceDownloader.shared.downloadResources()return true}
}

使用缓存的资源加载完整页面

import UIKit
import WebKitclass ViewController: UIViewController {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()webView = WKWebView(frame: self.view.bounds)self.view.addSubview(webView)// 构建完整的HTML内容if let htmlURL = URL(string: "https://www.example.com/index.html"),let cachedHTML = ResourceDownloader.shared.getCachedResource(for: htmlURL),let htmlString = String(data: cachedHTML, encoding: .utf8) {let completeHTMLString = embedCachedResources(in: htmlString)// 加载HTML内容到webViewwebView.loadHTMLString(completeHTMLString, baseURL: nil)} else {// 如果没有缓存,则加载远程 URLlet request = URLRequest(url: URL(string: "https://www.example.com")!)webView.load(request)}}private func embedCachedResources(in htmlString: String) -> String {var modifiedHTMLString = htmlString// 嵌入预加载的CSSif let cssURL = URL(string: "https://www.example.com/styles.css"),let cachedCSS = ResourceDownloader.shared.getCachedResource(for: cssURL),let cssString = String(data: cachedCSS, encoding: .utf8) {let cssTag = "<style>\(cssString)</style>"modifiedHTMLString = modifiedHTMLString.replacingOccurrences(of: "<link rel=\"stylesheet\" href=\"styles.css\">", with: cssTag)}// 嵌入预加载的JavaScriptif let jsURL = URL(string: "https://www.example.com/script.js"),let cachedJS = ResourceDownloader.shared.getCachedResource(for: jsURL),let jsString = String(data: cachedJS, encoding: .utf8) {let jsTag = "<script>\(jsString)</script>"modifiedHTMLString = modifiedHTMLString.replacingOccurrences(of: "<script src=\"script.js\" defer></script>", with: jsTag)}// 嵌入预加载的图像if let imageURL = URL(string: "https://www.example.com/image.png"),let cachedImage = ResourceDownloader.shared.getCachedResource(for: imageURL) {let base64Image = cachedImage.base64EncodedString()let imgTag = "<img src='data:image/png;base64,\(base64Image)' alt='Preloaded Image'>"modifiedHTMLString = modifiedHTMLString.replacingOccurrences(of: "<img src=\"image.png\" alt=\"Preloaded Image\">", with: imgTag)}return modifiedHTMLString}
}

详细说明

  1. 资源下载和缓存

    • 使用 URLSession 下载资源(包括HTML文件、CSS、JavaScript和图像),并将其缓存到本地存储。
    • 下载完成后,资源数据被写入本地存储,以便后续使用。
  2. 预加载资源

    • 在应用启动时调用 downloadResources() 方法,预先下载和缓存所需的资源。
  3. 使用缓存的资源加载完整页面

    • viewDidLoad() 方法中,通过 getCachedResource(for:) 获取缓存的HTML内容。
    • 使用 embedCachedResources(in:) 方法,将预加载的 CSS、JavaScript 和图像嵌入到 HTML 内容中。
    • 使用 webView.loadHTMLString(completeHTMLString, baseURL: nil) 加载修改后的 HTML 内容。

通过这种方式,可以确保在加载页面时直接使用本地缓存的资源,从而显著提高页面加载速度,提供更好的用户体验。


只缓存 CSS、JavaScript 和图像

只有 CSS、JavaScript 和图像是预加载的,而 HTML 文件是在打开 WKWebView 时才开始下载的。这种策略在某些情况下可能更加高效,尤其是当 HTML 文件需要动态生成或者频繁更新时。

为什么预加载 CSS、JavaScript 和图像?

预加载 CSS、JavaScript 和图像有几个优点:

  • 减少首次渲染时间:通过提前加载关键资源,可以显著减少页面首次渲染的时间,提高用户体验。
  • 减轻服务器压力:本地缓存的资源可以减少重复请求,减轻服务器的负载。
  • 提高离线体验:在某些情况下,即使没有网络连接,用户也能看到页面的一部分内容。

什么时候 HTML 文件在打开 WKWebView 时下载?

  • 动态内容:如果 HTML 文件内容是动态生成的(例如,包含个性化数据或实时更新的数据),那么在每次加载页面时请求最新的 HTML 文件是必要的。
  • 频繁更新:如果 HTML 文件内容频繁更新,预加载 HTML 文件可能会导致内容不一致。
  • 用户特定数据:某些应用需要根据用户的身份或状态生成不同的 HTML 内容。

完整的 Swift 代码实现

展示如何预加载 CSS、JavaScript 和图像,并在打开 WKWebView 时下载 HTML 文件。

import UIKit
import WebKitclass ViewController: UIViewController {var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()// 创建并配置 WKWebViewlet configuration = WKWebViewConfiguration()let urlSchemeHandler = LocalResourceHandler()configuration.setURLSchemeHandler(urlSchemeHandler, forURLScheme: "local")webView = WKWebView(frame: self.view.bounds, configuration: configuration)self.view.addSubview(webView)// 加载远程 HTML 文件if let htmlURL = URL(string: "https://www.example.com/index.html") {let request = URLRequest(url: htmlURL)webView.load(request)}}
}class LocalResourceHandler: NSObject, WKURLSchemeHandler {func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {guard let url = urlSchemeTask.request.url else {return}// 仅处理特定的 URL 方案,例如 "local"guard url.scheme == "local" else {// 对于非 "local" 方案的请求,不处理return}if let data = ResourceDownloader.shared.getCachedResource(for: url) {let mimeType = determineMimeType(for: url)let response = URLResponse(url: url, mimeType: mimeType, expectedContentLength: data.count, textEncodingName: nil)urlSchemeTask.didReceive(response)urlSchemeTask.didReceive(data)urlSchemeTask.didFinish()} else {// 缓存资源不可用,发起网络请求downloadResource(from: url, urlSchemeTask: urlSchemeTask)}}func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {}private func determineMimeType(for url: URL) -> String {switch url.pathExtension {case "css":return "text/css"case "js":return "application/javascript"case "png":return "image/png"default:return "text/plain"}}private func downloadResource(from url: URL, urlSchemeTask: WKURLSchemeTask) {let task = URLSession.shared.dataTask(with: url) { data, response, error inif let error = error {urlSchemeTask.didFailWithError(error)return}guard let data = data, let response = response else {urlSchemeTask.didFailWithError(NSError(domain: "LocalResourceHandler", code: 404, userInfo: nil))return}// 缓存资源ResourceDownloader.shared.cacheResource(data: data, url: url)// 返回资源urlSchemeTask.didReceive(response)urlSchemeTask.didReceive(data)urlSchemeTask.didFinish()}task.resume()}
}class ResourceDownloader {static let shared = ResourceDownloader()private init() {}func downloadResources() {let resources = [URL(string: "https://www.example.com/styles.css")!,URL(string: "https://www.example.com/script.js")!,URL(string: "https://www.example.com/image.png")!]for resource in resources {downloadResource(from: resource)}}private func downloadResource(from url: URL) {let task = URLSession.shared.downloadTask(with: url) { localURL, response, error inguard let localURL = localURL else { return }do {let data = try Data(contentsOf: localURL)self.cacheResource(data: data, url: url)} catch {print("Failed to load resource: \(error)")}}task.resume()}func cacheResource(data: Data, url: URL) {let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)do {try data.write(to: fileURL)print("Resource cached: \(fileURL)")} catch {print("Failed to cache resource: \(error)")}}func getCachedResource(for url: URL) -> Data? {let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)return try? Data(contentsOf: fileURL)}
}
在应用启动时预加载资源
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {// 预加载资源ResourceDownloader.shared.downloadResources()return true}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/350057.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WinForm之TCP客户端通讯

目录 一 设计界面 二 后台代码 一 设计界面 二 后台代码 using System.Net.Sockets; using System.Text;namespace TCP网络客户端通讯 {public partial class Form1 : Form{public Form1(){InitializeComponent();}TcpClient tcpClient new TcpClient();private void conne…

停止游戏中的循环扣血显示

停止游戏中循环扣血并显示的具体实现方式会依赖于你的代码结构和游戏的逻辑。通常情况下&#xff0c;你可以通过以下方式来实现停止循环扣血和显示&#xff1a; 1、问题背景 在使用 Python 代码为游戏开发一个生命值条时&#xff0c;遇到了一个问题。代码使用了循环来减少生命…

atcoder abc357

A Sanitize Hands 问题&#xff1a; 思路&#xff1a;前缀和&#xff0c;暴力&#xff0c;你想咋做就咋做 代码&#xff1a; #include <iostream>using namespace std;const int N 2e5 10;int n, m; int a[N];int main() {cin >> n >> m;for(int i 1…

四轴飞行器、无人机(STM32、NRF24L01)

一、简介 此电路由STM32为主控芯片&#xff0c;NRF24L01、MPU6050为辅,当接受到信号时&#xff0c;处理对应的指令。 二、实物图 三、部分代码 void FlightPidControl(float dt) { volatile static uint8_t statusWAITING_1; switch(status) { case WAITING_1: //等待解锁 if…

2024最新最全【AIGC】学习零基础入门到精通,看完这一篇就够了!

这个文案就是由AI生成的哦&#xff01;&#xff01;&#xff01;&#xff01; AIGC&#xff08;AI-Generated Content&#xff09; 即人工智能生成内容&#xff0c;是指利用人工智能技术来创造各种形式的内容&#xff0c;包括文字、图像、视频、音频和游戏等。与专业生成内容…

图解Transformer学习笔记

教程是来自https://github.com/datawhalechina/learn-nlp-with-transformers/blob/main/docs/ 图解Transformer Attention为RNN带来了优点&#xff0c;那么有没有一种神经网络结构直接基于Attention构造&#xff0c;而不再依赖RNN、LSTM或者CNN的结构&#xff0c;这就是Trans…

【算法专题--链表】反转链表II--高频面试题(图文详解,小白一看就会!!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐迭代法 --- 带哨兵位&#xff08;头节点&#xff09; &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 四、总结与提炼 五、共勉 一、前言 反转链表II这道题&#xff0c;可以说是--链表专题--&am…

RAG工作流在高效信息检索中的应用

介绍 RAG&#xff08;Retrieval Augmented Generation&#xff09;是一种突破知识限制、整合外部数据并增强上下文理解的方法。 由于其高效地整合外部数据而无需持续微调&#xff0c;RAG的受欢迎程度正在飙升。 让我们来探索RAG如何克服LLM的挑战&#xff01; LLM知识限制大…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第38课-密室逃脱-3D互动剧情

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第38课-密室逃脱 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

Flutter - Material3适配

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 Flutter - Material3适配 对比图具体实现一些组件的变化 代码实现Material2的ThemeDataMaterial3的ThemeData Material3适配官方文档 flutter SDK升级到3.16.0之后 …

C# WinForm —— 35 StatusStrip 介绍

1. 简介 状态栏 StatusStrip&#xff0c;默认在软件的最下方&#xff0c;用于显示系统时间、版本、进度条、账号、角色信息、操作位置信息等 可以在状态栏中添加的控件类型有&#xff1a;StatusLabel、ProgressBar、DropDownButton、SplitButton 2. 属性 属性解释(Name)控…

utm投影

一 概述 UTM (Universal Transverse Mercator)坐标系是由美国军方在1947提出的。虽然我们仍然将其看作与“高斯&#xff0d;克吕格”相似的坐标系统&#xff0c;但实际上UTM采用了网格的分带&#xff08;或分块&#xff09;。除在美国本土采用Clarke 1866椭球体以外&#xff0c…

树莓派等Linux开发板上使用 SSD1306 OLED 屏幕,bullseye系统 ubuntu,debian

Raspberry Pi OS Bullseye 最近发布了,随之而来的是许多改进,但其中大部分都在引擎盖下。没有那么多视觉差异,最明显的可能是新的默认桌面背景,现在是大坝或湖泊上的日落。https://www.the-diy-life.com/add-an-oled-stats-display-to-raspberry-pi-os-bullseye/ 通过这次操…

【GD32】 TIMER通用定时器学习+PWM输出占空比控制LED

扩展&#xff1a;对PWM波形的输出进行捕获 目录 一、简介二、具体功能描述1、时钟源的选择&#xff1a;2、预分频器&#xff1a;3、计数模式&#xff1a;向上计数模式&#xff1a;向下计数模式&#xff1a;中央对齐模式&#xff1a; 4、捕获/比较通道 输入捕获模式 输出比…

前端问题整理

Vue vue mvvm&#xff08;Model-View-ViewModel&#xff09;架构模式原理 Model 是数据层&#xff0c;即 vue 实例中的数据View 是视图层&#xff0c; 即 domViewModel&#xff0c;即连接Model和Vue的中间层&#xff0c;Vue实例就是ViewModelViewModel 负责将 Model 的变化反映…

TCGAbiolinks包学习

TCGAbiolinks 写在前面学习目的GDCquery GDCdownload GDC prepare中间遇到的报错下载蛋白质数据 写在前面 由于别人提醒我TCGA的数据可以利用TCGAbiolinks下载并处理&#xff0c;所以我决定阅读该包手册&#xff0c;主要是该包应该是有更新的&#xff0c;我看手册进行更新了&…

【CS.PL】Lua 编程之道: 简介与环境设置 - 进度8%

1 初级阶段 —— 简介与环境设置 文章目录 1 初级阶段 —— 简介与环境设置1.1 什么是 Lua&#xff1f;特点?1.2 Lua 的应用领域1.3 安装 Lua 解释器1.3.1 安装1.3.2 Lua解释器的结构 1.4 Lua执行方式1.4.0 程序段1.4.1 使用 Lua REPL&#xff08;Read-Eval-Print Loop&#x…

LAMP部署及应用

LAMP架构 LAMP架构是指一种常用的网站开发架构&#xff0c;它由以下几个组件组成&#xff1a; Linux操作系统&#xff1a;作为服务器的操作系统&#xff0c;LAMP架构通常使用Linux作为操作系统&#xff0c;因为Linux通常被认为是稳定和安全的。 Apache HTTP服务器&#xff1a…

iOS ReactiveCocoa MVVM

学习了在MVVM中如何使用RactiveCocoa&#xff0c;简单的写上一个demo。重点在于如何在MVVM各层之间使用RAC的信号来更方便的在各个层之间进行响应式数据交互。 demo需求&#xff1a;一个登录界面(登录界面只有账号和密码都有输入&#xff0c;登录按钮才可以点击操作)&#xff0…

AI模型部署:Triton+TensorRT部署Bert文本向量化服务实践

前言 本篇介绍以Triton作为推理服务器&#xff0c;TensorRT作为推理后端&#xff0c;部署句嵌入向量模型m3e-base的工程方案和实现&#xff0c;句嵌入模型本质上是Bert结构&#xff0c;本案例可以推广到更一般的深度学习模型部署场景。 内容摘要 推理服务器和推理后端介绍Ten…