HarmonyOS 鸿蒙Next 如何解决webview加载vue项目时因跨域导致的白屏问题
【问题现象】
webview加载沙盒中的vue项目,会出现跨域报错导致白屏。
问题代码如下:
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import { BusinessError, zlib } from '@kit.BasicServicesKit';
import { FileUtil } from '@pura/harmony-utils';
import fs from '@ohos.file.fs';
import { buffer } from '@kit.ArkTS';
@Entry
@Component
struct WebVuePage {
@State message: string = 'Hello World';
controller: webview.WebviewController = new webview.WebviewController();
context = getContext(this) as common.UIAbilityContext;
filesDir = this.context.filesDir;
@State show:boolean = false;
build() {
Column() {
Text('复制文件到沙盒').onClick(()=>{
})
Text('解压').onClick(()=>{
// this.testUnCompressZip();
})
Web({ src: this.context.filesDir+'/dist/index.html', controller: this.controller })
}
.height('100%')
.width('100%')
}
}
页面如下:
devTools调试报错如下:
【背景知识】
【定位思路】
(1)为了提高安全性,ArkWeb内核不允许file协议或者resource协议访问URL上下文中来自跨域的请求,vue项目主文件html中的相关文件请求就是file协议请求,所以会出现对应跨域报错,导致页面白屏。
(2)使用HTTP或者HTTPS等协议替代原本使用的file协议进行加载。通过devTools查看无法加载的资源,然后使用Web组件的onInterceptRequest接口和setResponseData接口对无法加载的资源进行拦截并替换为相应的本地资源。
(3)通过devTools定位,vue项目中会请求多种资源,如图片资源(.png .jpg .ico等)、视频资源(.mp4等)、文件资源(.css、.js等);需要确定每种资源的返回体形式,然后针对不同类型资源找到对应的映射方式,将对应返回体返回给web请求。
(4)查看setResponseData接收的参数格式为string | number | Resource| ArrayBuffer,初步判断应该将文件转为string或者ArrayBuffer类型塞入返回体(response)。
【解决方案】
(1)用HTTP****协议替代原本的file协议。
代码示例如下:
Web({ src: 'https://www.example.com/dist/index.html', controller: this.controller })
(2)通过devTools和onInterceptRequest找到所有无法正常加载的url,替换成对应的沙箱路径地址,以Map****格式存储。
代码示例如下:
schemeMapSha = new Map([
["https://www.example.com/dist/index.html", "dist/index.html"],
["https://www.example.com/assets/index-CdZkYDST.js", 'dist/assets/index-CdZkYDST.js'],
["https://www.example.com/assets/index-CBy-tPLh.css", 'dist/assets/index-CBy-tPLh.css'],
["https://www.example.com/dist/index.html#/home", "dist/index.html"],
["https://www.example.com/assets/index-DEaf4dPO.js", "dist/assets/index-DEaf4dPO.js"],
["https://www.example.com/assets/index-CBy-tPLh.css", "dist/assets/index-CBy-tPLh.css"],
["https://www.example.com/assets/AboutView-Dc7Uu0nQ.js", "dist/assets/AboutView-Dc7Uu0nQ.js"],
["https://www.example.com/assets/AboutView-C6Dx7pxG.css", "dist/assets/AboutView-C6Dx7pxG.css"],
["https://www.example.com/dist/assets/AboutView-CSLU876L.js", "dist/assets/AboutView-CSLU876L.js"],
["https://www.example.com/assets/AboutChild-B4t3HCqG.js", "dist/assets/AboutChild-B4t3HCqG.js"],
["https://www.example.com/assets/AboutView-P0x6aoHb.js", "dist/assets/AboutView-P0x6aoHb.js"],
["https://www.example.com/assets/AboutChild-CMguLNki.js", "dist/assets/AboutChild-CMguLNki.js"],
["https://www.example.com/assets/index-BzBjVdyO.js", "dist/assets/index-BzBjVdyO.js"],
["https://www.example.com/assets/test-B4w1tkP8.mp4", "dist/assets/test-B4w1tkP8.mp4"],
["https://www.example.com/assets/AboutView-BMur5onV.js", "dist/assets/AboutView-BMur5onV.js"],
["https://www.example.com/assets/AboutChild-CUcxJpMY.js", "dist/assets/AboutChild-CUcxJpMY.js"],
["https://www.example.com/assets/mmmm-6R9i91gc.png", "dist/assets/mmmm-6R9i91gc.png"],
["https://www.example.com/assets/test_jpeg-BjZOxgOX.jpg", "dist/assets/test_jpeg-BjZOxgOX.jpg"],
]);
(3)通过onInterceptRequest拦截每个地址,用map中对应映射替换。文件尝试通过fileIo.readTextSync读取为字符串进行替换,发现html、js、css类的文件都可以正常加载,但是jpg、mp4类的文件页面上出现空白无法加载出对应图片和视频。
代码示例如下:
data = fileIo.readTextSync(this.filesDir+ '/'+shaFileName);
response.setResponseData(data);
response.setResponseEncoding('utf-8');
response.setResponseMimeType(mimeType);
response.setResponseCode(200);
response.setReasonMessage('OK');
response.setResponseIsReady(true);
return response;
(4)尝试媒体类文件转arrayBuffer。
代码示例如下:
let file = fs.openSync(`${getContext().filesDir}/${shaFileName}`)
let stat = fs.statSync(file.fd)
let buf = new ArrayBuffer(stat.size)
fs.readSync(file.fd, buf)
data = buf;
fs.closeSync(file)
response.setResponseData(data);
response.setResponseEncoding('utf-8');
response.setResponseMimeType(mimeType);
response.setResponseCode(200);
response.setReasonMessage('OK');
response.setResponseIsReady(true);
(5)成功加载。
【总结】
Web加载本地资源经常出现跨域问题,解决跨域问题一般有以下步骤:
- web中的src填写为自己构造的域名,如:‘https://www.example.com/dist/index.html’。
- 通过web的onInterceptRequest方法对页面中请求的url进行拦截,替换为本地沙箱的资源。
- 通过setResponseData替换相应文件返回体。其中文件资源(js、css、html等)可以直接用readTextSync读取对应文件内容作为setResponseData的参数;媒体资源(png、jpg、mp4等)读取文件的arrayBuffer作为setResponseData的参数。
HarmonyOS 鸿蒙Next 在解决webview加载vue项目时因跨域导致的白屏问题时,可以尝试以下方法:
-
构造域名和本地文件的映射表:
- 为本地vue项目文件构造一个虚拟域名,并在代码中建立该域名与本地文件路径的映射关系。
-
拦截并替换请求:
- 在webview的请求拦截回调中,检查请求的URL是否匹配映射表中的域名。
- 如果匹配,则将请求重定向到对应的本地文件路径。
-
配置MIME类型:
- 确保为映射表中的每个文件路径配置了正确的MIME类型,以便webview能够正确解析和加载文件。
-
确保权限和配置正确:
- 检查应用是否已获取必要的网络权限和文件系统权限。
- 确保webview组件的配置正确,包括允许JavaScript访问、DOM存储访问等。
通过以上步骤,通常可以解决因跨域导致的白屏问题。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html 。