HarmonyOS 鸿蒙Next 如何解决webview加载vue项目时因跨域导致的白屏问题

发布于 1周前 作者 gougou168 最后一次编辑是 5天前 来自 鸿蒙OS

【问题现象】

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调试报错如下:

点击放大

【背景知识】

  • ArkWeb:用于在应用程序中显示Web页面内容。
  • 官方关于跨域问题给出的解决方案

【定位思路】

(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加载本地资源经常出现跨域问题,解决跨域问题一般有以下步骤:

  1. web中的src填写为自己构造的域名,如:‘https://www.example.com/dist/index.html’。
  2. 通过web的onInterceptRequest方法对页面中请求的url进行拦截,替换为本地沙箱的资源。
  3. 通过setResponseData替换相应文件返回体。其中文件资源(js、css、html等)可以直接用readTextSync读取对应文件内容作为setResponseData的参数;媒体资源(png、jpg、mp4等)读取文件的arrayBuffer作为setResponseData的参数。
1 回复

HarmonyOS 鸿蒙Next 在解决webview加载vue项目时因跨域导致的白屏问题时,可以尝试以下方法:

  1. 构造域名和本地文件的映射表

    • 为本地vue项目文件构造一个虚拟域名,并在代码中建立该域名与本地文件路径的映射关系。
  2. 拦截并替换请求

    • 在webview的请求拦截回调中,检查请求的URL是否匹配映射表中的域名。
    • 如果匹配,则将请求重定向到对应的本地文件路径。
  3. 配置MIME类型

    • 确保为映射表中的每个文件路径配置了正确的MIME类型,以便webview能够正确解析和加载文件。
  4. 确保权限和配置正确

    • 检查应用是否已获取必要的网络权限和文件系统权限。
    • 确保webview组件的配置正确,包括允许JavaScript访问、DOM存储访问等。

通过以上步骤,通常可以解决因跨域导致的白屏问题。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部