计划1: Scheme 阻拦
1. 计划是什么?
Scheme阻拦是一种网络恳求阻拦战略,主要经过界说自界说的URL Scheme和相应的处理办法,使得能够在网络恳求时阻拦并处理特定的URL恳求。在iOS中,咱们能够运用WKURLSchemeHandler来完结这个功能。苹果为WKWebView供给了一个自界说scheme的功能,即WKURLSchemeHandler,能够阻拦自界说scheme的恳求,然后回来咱们自界说的数据。比方咱们能够阻拦一切”myScheme://”的恳求,然后回来咱们的离线资源。
2. 这个计划是为了处理什么问题而规划的?
URL Scheme原本是被规划出来用于在Web和Native运用之间进行交互的。在iOS平台上,咱们能够为一个运用界说自己的URL Scheme,然后其他运用或许Web页面就能够经过这个URL Scheme来启动该运用并传递参数。关于WKWebView来说,iOS 11开端引入了WKURLSchemeHandler这个接口,让咱们能够阻拦WKWebView中的自界说URL Scheme恳求,从而让咱们有时机自界说这些恳求的处理办法,比方从本地加载一个资源文件,或许做一些特别的处理。
3. 这种计划怎样展开作业,处理办法是什么?
详细的完结步骤如下:
界说自界说的URL Scheme和相应的处理办法:在iOS中,能够经过完结WKURLSchemeHandler协议来界说怎样处理自界说的URL Scheme。
将自界说的URL Scheme与WebView关联:运用WKWebViewConfiguration的setURLSchemeHandler:forURLScheme:办法,将自界说的URL Scheme和对应的处理办法关联起来。
在网络恳求时阻拦特定的URL:当WebView遇到自界说的URL Scheme时,会调用相应的WKURLSchemeHandler进行处理。
处理阻拦的URL:在WKURLSchemeHandler的办法中,咱们能够决定怎样处理阻拦的URL。例如,能够从本地缓存中获取相应的资源,并将其回来给WebView。
4. 这个计划存在什么问题, 怎样处理这些问题?
存在的问题包含:
WKWebView的scheme阻拦只能阻拦页面内部链接的资源,关于浏览器主动加载的资源(如CSS背景图画)或JavaScript动态加载的资源,无法进行阻拦。处理计划是尝试修正前端代码,使得一切资源都经过HTML链接加载,或许运用Service Workers或其他阻拦技能进行阻拦。
虽然咱们能够阻拦并供给缓存的资源,可是何时将这些资源烘托给浏览器还是一个问题。处理计划是,在页面的onload事件后进行资源的烘托。
事例:
首先,咱们需求在HTML中运用自界说的scheme,例如:
<img src="myScheme://image.png" />
然后,在iOS端,咱们需求完结一个遵从WKURLSchemeHandler协议的类:
class MySchemeHandler: NSObject, WKURLSchemeHandler {
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
// 依据urlSchemeTask.request来判别要回来什么资源
if let url = urlSchemeTask.request.url,
let path = url.path,
let resourcePath = Bundle.main.path(forResource: path, ofType: nil),
let data = try? Data(contentsOf: URL(fileURLWithPath: resourcePath)) {
let response = URLResponse(url: url, mimeType: "image/png", expectedContentLength: data.count, textEncodingName: nil)
urlSchemeTask.didReceive(response)
urlSchemeTask.didReceive(data)
urlSchemeTask.didFinish()
} else {
urlSchemeTask.didFailWithError(NSError(domain: "Domain", code: 0, userInfo: nil))
}
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
// 停止加载
}
}
然后在创建WKWebView的时分,注册这个scheme handler:
let configuration = WKWebViewConfiguration()
let schemeHandler = MySchemeHandler()
configuration.setURLSchemeHandler(schemeHandler, forURLScheme: "myScheme")
let webView = WKWebView(frame: .zero, configuration: configuration)
这样,当WKWebView在加载一个运用”myScheme://”的资源时,就会调用咱们的MySchemeHandler来获取数据。
计划2: 前端打包本地静态文件
1. 计划是什么?
这个计划中,咱们将一切前端资源(HTML,CSS,JavaScript,图片等)打包到一个本地文件或许目录中(比方一个ZIP文件),然后直接在WebView中加载这个本地文件或许目录。
2. 这个计划是为了处理什么问题而规划的?
在网络不稳定或许没有网络的环境下,经过本地加载网页资源,咱们能够保证用户能够正常运用咱们的Web运用,而不受网络环境的影响。
3. 这种计划怎样展开作业,处理办法是什么?
前端打包本地静态文件的一般步骤如下:
运用东西打包前端资源:能够运用如Webpack这样的东西将一切前端资源打包到一个ZIP文件中。
将ZIP文件放到运用的本地目录:能够在运用装置时将ZIP文件放到运用的沙盒目录中,或许在运用运行时下载ZIP文件并解压到沙盒目录中。
在WebView中加载本地资源:运用file协议在WebView中加载沙盒目录中的资源。
下面是运用Webpack打包前端资源的一个根本装备示例:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
}),
],
mode: 'production'
};
4. 这个计划存在什么问题, 怎样处理这些问题?
跨域问题:由于浏览器的安全战略,本地文件或许无法正常的拜访网络资源。处理办法是运用CORS进行跨域资源共享,或许经过设置WebView的安全战略来允许跨域拜访。
更新问题:当前端资源需求更新时,需求从头打包并下载新的ZIP文件。处理办法是在运用启动时查看资源的更新,并下载新的资源。
缓存问题:关于一些大的或许不常更新的资源,或许不需求每次都从头下载。处理办法是运用缓存战略,如HTTP缓存,或许运用Service Worker进行资源的缓存。
在iOS中,能够运用如下代码在WebView中加载本地资源:
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "www")
let request = URLRequest(url: url!)
webView.load(request)
在这个示例中,咱们将本地资源放到了www子目录下,而且运用index.html作为入口文件。咱们运用Bundle.main.url(forResource:withExtension:subdirectory:)办法获取到资源的URL,然后创建一个URLRequest并加载到WebView中。
计划三:运用 WebServer
1. 计划描绘
此计划主要是在移动端内置一个小型的 WebServer,经过这个 WebServer 来供给静态资源服务。这样能够让一切的资源恳求都是同源的,避免了跨域问题。同时,也能够方便地办理和更新资源文件。
2. 问题描绘
内置 WebServer 的计划主要的问题是移动设备的功能和资源约束,包含 CPU、内存和电量。别的,WebServer 需求在后台持续运行,这在一些操作系统中或许受到约束。最后,WebServer 的安全性也需求考虑,虽然这是在本地网络环境下,但仍然或许有一些潜在的安全风险。
3. 处理办法
运用 Swift 完结内置 WebServer 的示例代码:
import GCDWebServer
let webServer = GCDWebServer()
webServer?.addDefaultHandler(forMethod: "GET", request: GCDWebServerRequest.self, processBlock: {request in
let filePath = self.documentPath.appending(request.path)
let html = try! String(contentsOfFile: filePath, encoding: .utf8)
return GCDWebServerDataResponse(html: html)
})
webServer?.start(withPort: 8080, bonjourName: nil)
以上代码运用了 GCDWebServer 库来创建一个 HTTP 服务器,监听 8080 端口。addDefaultHandler 函数用来处理一切的 GET 恳求,回来恳求路径下的文件内容。
4. 存在的问题及处理计划
关于功能和资源约束问题,需求在规划和完结时尽量优化功能,削减资源占用。别的,需求处理好运用切换到后台时的情况,或许需求停止 WebServer,或许依据操作系统的规则进行其他处理。关于安全性问题,能够经过一些安全机制,比方只允许本地拜访,或许运用 HTTPS 等办法来进步安全性。
计划四:前端运用Service Worker或WPA的Cache API
1. 计划描绘
该计划是指经过前端技能 Service Worker 或 WPA(Web Progressive Applications)的 Cache API,来阻拦和处理浏览器的网络恳求,到达缓存资源和离线拜访的意图。
2. 问题描绘
当前,HTML5 供给了丰厚的离线存储机制,但怎样有效、灵敏地运用这些机制进行资源办理,保证网页在断网或许网络欠安的情况下也能正常拜访,是本计划需求处理的主要问题。
3. 处理办法
前端需求运用 Service Worker 或 WPA 的 Cache API,进行以下操作:
注册 Service Worker 或 WPA,为运用装置 Cache API。
经过 Service Worker 或 WPA 阻拦网络恳求,判别是否射中缓存。假如射中缓存,则直接回来缓存的资源;不然,向服务器发出恳求,获取资源并存入缓存。
缓存战略的制定和实施。能够依据需求,定制不同的缓存战略,如优先运用缓存、优先获取最新资源等。
4. 存在的问题及处理计划
在运用 Service Worker 或 WPA 的 Cache API 过程中,或许会遇到以下问题:
浏览器兼容性:不是一切的浏览器都完全支持 Service Worker 或 WPA 的 Cache API。关于不支持的浏览器,需求寻找代替计划,如运用其他的缓存技能(例如 localStorage、IndexedDB)或许退化到普通的网络恳求。
缓存办理:假如不留意办理,缓存的资源或许会占用很多的存储空间。因而,需求在适宜的时机整理过期的或许不再需求的缓存。同时,需求处理好版别更新时的缓存替换问题。
下面是运用Service Worker合作Cache API进行离线资源办理的基础代码示例:
首先,咱们需求在主线程的JavaScript中注册Service Worker:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('Service Worker 注册成功,scope: ', registration.scope);
}, function(err) {
console.log('Service Worker 注册失利: ', err);
});
});
}
然后在sw.js中界说Service Worker的装置和激活行为:
javascript
Copy code
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('activate', function(event) {
var cacheWhitelist = ['my-site-cache-v1', 'blog-posts-cache-v1'];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
接下来,咱们界说怎样响应恳求:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
if (response) {
return response;
}
return fetch(event.request).then(
function(response) {
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
这是根本的 Service Worker 注册和运用 Cache API 的流程,你能够依据自己的需求进行更杂乱的战略定制。需求留意的是,运用 Service Worker 和 Cache API 需求 HTTPS 环境。
最终选定的计划2:
即网页静态化计划,咱们需求前端、后端和iOS端各自协作才干完结。下面我详细介绍各端应该怎样做:
前端:
咱们的目标是将编辑器模块的一切资源打包成一个独自的文件,这个过程需求经过Webpack这样的打包东西来完结。
1.1 分包:
为了使项目愈加模块化和优化加载功能,咱们能够在webpack中运用splitChunks进行代码分包。在webpack.config.js中增加以下装备:
optimization: {
splitChunks: {
chunks: 'all',
},
}
1.2 资源的scheme界说:
关于需求打包的资源,咱们能够经过修正Webpack装备文件,将资源文件的引用路径设置为自界说scheme。
关于Vue项目,你能够在vue.config.js中修正webpack的装备:
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.tap(options => Object.assign(options, { name: 'myscheme://[name].[ext]' }))
}
iOS端:
iOS端主要使命是监听自界说的scheme,并在收到恳求时回来对应的本地资源。
2.1 监听scheme:
你能够经过WKWebView的WKURLSchemeHandler来监听自界说的scheme:
let webViewConfig = WKWebViewConfiguration()
let customSchemeHandler = MySchemeHandler()
webViewConfig.setURLSchemeHandler(customSchemeHandler, forURLScheme: "myscheme")
let webView = WKWebView(frame: .zero, configuration: webViewConfig)
2.2 处理scheme恳求:
在你的SchemeHandler中,你需求完结WKURLSchemeHandler协议的两个办法:
class MySchemeHandler: NSObject, WKURLSchemeHandler {
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
let url = urlSchemeTask.request.url!
let resource = url.lastPathComponent
let resourcePath = Bundle.main.path(forResource: resource, ofType: nil)!
let resourceUrl = URL(fileURLWithPath: resourcePath)
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "1.1", headerFields: nil)!
urlSchemeTask.didReceive(response)
urlSchemeTask.didReceive(Data(contentsOf: resourceUrl))
urlSchemeTask.didFinish()
}
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
}
}
后端:
关于后端,主要的使命或许会是协助前端进行资源文件的打包和发布。详细的作业或许会依据项意图实践需求而定。或许的使命包含:
供给资源文件的服务器托管
协助完结资源文件的版别操控和更新战略
供给API接口供移动端查询最新的资源文件信息
将在计划2中增加跨域问题的处理计划:
1, 资源跨域:运用自界说scheme能够处理资源跨域的问题。如上所述,经过设置自界说scheme,将资源的引用路径重定向到运用本地,避免了跨域的问题。自界说scheme如”myscheme”,在前端打包时,资源的引用路径将会变为”myscheme://[资源名]“,然后在iOS端阻拦这个scheme,回来本地的资源文件。
2, 网络恳求跨域:网络恳求的跨域问题或许需求后端协作处理,或许经过移动端进行JS交互完结。一种或许的办法是在后端设置CORS(跨源资源共享)战略,允许来自特定来源的恳求。另一种办法是经过iOS端的JavaScriptCore或WKScriptMessageHandler与网页进行交互,iOS端接收到JS的恳求后,自行进行网络恳求,然后将结果回来给JS。
在iOS端,能够运用WKUserContentController的add办法来增加音讯处理器,然后在WKScriptMessageHandler协议的userContentController:didReceiveScriptMessage:办法中处理JS的恳求。例如:
let contentController = WKUserContentController()
contentController.add(self, name: "jsHandler")
let config = WKWebViewConfiguration()
config.userContentController = contentController
let webView = WKWebView(frame: .zero, configuration: config)
// WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "jsHandler" {
// 处理JS的恳求,发送网络恳求,然后将结果经过evaluateJavaScript回来给JS
}
}
这样,经过JS交互,能够处理网络恳求的跨域问题,但需求留意的是,由于在iOS端进行网络恳求,或许会需求处理一些额定的问题,如证书验证、Cookie办理等。