在这里插入图片描述

从一个常见问题说起

很多从 Android 或 iOS 转到 HarmonyOS 的开发者,第一次接触 ArkWeb 时,都会问一个相同的问题:“ArkWeb 是不是就是 WebView?”

答案既是,也不是。它确实是用来加载和渲染网页的,但 ArkWeb 是华为从底层重新设计的一套 Web 引擎,它不是一个简单的 WebView 移植版。如果你在项目里直接用 WebView 那套思路去写 ArkWeb,不出三天就会遇到各种奇怪的生命周期问题、交互失效和性能卡顿。

这篇文章不讲复杂的配置,先帮大家建立一个完整的认知框架。只有知道了 ArkWeb 能做什么、不能做什么,后续学具体 API 时才能有的放矢。

ArkWeb 到底解决了什么问题

在 HarmonyOS NEXT 里,ArkWeb 是唯一官方支持的 Web 渲染引擎。它承担了三个核心职责:

  1. 网页渲染:解析 HTML、CSS、JS,把网页内容显示到屏幕上。
  2. 应用交互:让 ArkTS 代码和网页 JS 可以互相调用,实现混合开发。
  3. 安全隔离:控制网页的访问权限、Cookie、存储等,防止恶意代码影响应用。

它与 WebView 的关键区别

对比维度 传统 WebView (Android/iOS) ArkWeb
渲染内核 Chromium / WKWebView 华为自研内核
生命周期管理 跟随 Activity/ViewController 跟随 ArkUI 组件树
JS 交互方式 addJavascriptInterface / JSContext @NativeCall / runJavaScript
安全策略 CSP + 自定义 应用沙箱 + 权限链路管控
痛点 碎片化严重,不同版本表现不一致 统一,但部分 API 限制严格

传统 WebView 最大的问题是碎片化:同一种写法在不同厂商的 ROM 上可能表现不同。ArkWeb 在 DevEco Studio + 真机环境下保持了高度一致,这是一个明显的优势。但它的代价是,你对底层内核的控制力被大幅削弱——一些在 Android 上可以轻易 hook 的底层能力,在 ArkWeb 里必须通过官方提供的 API 去实现。

典型应用场景

  • 混合 App:App 中嵌套 H5 页面,比如电商的商品详情页、资讯类的文章正文页。
  • 能力扩展:需要在 H5 中调用设备原生能力(相机、定位、文件选择),但不想升级原生 App。
  • 安全浏览:需要在一个受控的沙箱环境里加载外部网站,管控 Cookie 和脚本注入。

不适合的场景:

  • 对底层渲染引擎有深度定制要求的(比如修改 Webkit 源码来优化特定渲染逻辑)。
  • 需要跑一个 5 年以上的企业级 Web 应用,且该应用强依赖旧版 Chromium 特性。

环境说明

DevEco Studio 版本:DevEco Studio 6.1.0 及以上
HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上
目标设备:手机 / 平板

ArkWeb 引擎的三大能力模块

1. 网页渲染能力

这是 ArkWeb 最基础的功能。你只需要在 ArkUI 中放置一个 Web 组件,并指定 src 属性,就可以加载一个网页。

// 最简单的 ArkWeb 使用示例
@Entry
@Component
struct WebPage {
  build() {
    Column() {
      Web({ src: 'https://www.example.com', controller: new WebviewController() })
        .width('100%')
        .height('100%')
    }
    .width('100%')
    .height('100%)
  }
}

这段代码做了什么:

  1. Web 是 ArkUI 的组件,相当于一个“网页容器”。
  2. src 可以是 URL 或本地 HTML 文件路径。
  3. WebviewController 用于控制网页行为(前进、后退、刷新等)。

关键注意事项

  • Web 组件需要占满整个 Container,否则会出现白边或无法滚动。
  • 在页面销毁前,需要手动销毁 WebviewController,否则会导致内存泄露。
  • 如果 src 指向本地文件,文件必须放在 rawfile 目录下。

2. JS 交互能力

ArkWeb 提供了双向的 JS 交互机制:

  • ArkTS → JS:通过 runJavaScript 方法执行 JS 代码。
  • JS → ArkTS:通过 @NativeCall 装饰器或 javaScriptProxy 暴露原生方法。
// 从 ArkTS 调用 JS
controller.runJavaScript('alert("Hello from ArkTS");');

// 从 JS 调用 ArkTS
@State
private message: string = '';

build() {
  Web({ src: $rawfile('index.html'), controller: this.controller })
    .javaScriptProxy({
      object: {
        name: 'nativeBridge',
        methodList: [
          { name: 'getMessage', method: (data: string) => {
            this.message = data;
          }}
        ]
      },
      controller: this.controller
    })
}

这段代码的关键点

  1. runJavaScript 是异步方法,必须在 Web 组件 onPageEnd 回调之后才能调用。
  2. javaScriptProxy 中的 object.name 在 JS 中会被绑定为 window.nativeBridge
  3. 传参时,数据需要序列化成字符串,不能直接传对象(ArkWeb 目前不支持非字符串传参)。

3. 安全机制

ArkWeb 默认启用了一套比较严格的安全策略。常见的安全相关配置包括:

  • Cookie 管理:通过 WebCookie 管理域名级 Cookie。
  • 权限控制:相机、定位等敏感权限需要显式申请。
  • 跨域限制:同源策略默认开启。
// 设置跨域白名单
controller.setCrossOriginOpenerPolicy('same-origin');
controller.setCrossOriginEmbedderPolicy('require-corp');

现实中的坑:很多开发者在第一次尝试 JS 交互时,会遇到 SecurityError。这通常是因为:

  1. Web 组件加载的是 http 而非 https 页面,而安全策略默认不允许。
  2. @NativeCall 暴露的方法名与 JS 内置方法冲突。

常见问题 1:跨域请求失败

现象:在 Web 组件加载的 H5 页面中,使用 fetchXMLHttpRequest 请求不同域的接口,请求直接被阻断。

原因:ArkWeb 内核默认执行了严格的同源策略。它不同于 Android 系统 WebView 可以通过 setAllowFileAccess 等方法绕过。

解决方案

  1. 后端配合设置 Access-Control-Allow-Origin
  2. 如果后端无法修改,可以在应用中设置代理:
// 通过 Network 模块建立本地代理,把请求映射到同源路径
  1. 最保险的方法:所有 API 请求都走 ArkTS 原生侧发起,通过 @NativeCall 返回数据给 H5。

常见问题 2:本地 HTML 无法加载 Scheme 页面

现象:在 rawfile 中的 HTML 页面里,写 <a href="myscheme://page"> 无法跳转。

原因:ArkWeb 对 URL Scheme 的处理策略与 WebView 不同。本地页面的 Scheme 需通过 onLoadIntercept 手动处理。

解决方案

Web({ src: $rawfile('index.html'), controller: this.controller })
  .onLoadIntercept((event) => {
    if (event.data.getRequestUrl().startsWith('myscheme://')) {
      // 手动处理 Scheme
      return true; // 返回 true 表示已处理,不再加载
    }
    return false;
  })

最佳实践

  1. onPageEnd 中初始化 JS 交互:不要试图在网页加载完成前调用 runJavaScript。绑定 onPageEnd 回调后,再执行 JS 注入。
  2. 使用 WebviewController 管理唯一实例:不要在每个页面都新建一个 WebviewController,否则会导致浏览器进程重复创建。
  3. 优先使用 @NativeCall 传递大数据:如果需要在 H5 和 ArkTS 之间传递图片或大段文本,不要直接通过 JS 参数传递(会序列化两次),而是使用 runJavaScript 传递文件名,再由原生侧读取文件。

FAQ

Q:为什么真机可以正常加载,模拟器显示空白?
A:模拟器的 Web 内核版本可能低于真机,导致部分 CSS 特性不支持。建议始终用真机调试。

Q:页面返回后,状态丢失怎么办?
A:ArkWeb 的 Web 组件默认不会缓存页面状态。如果需要对返回页面保持滚动位置和数据,需要在 onPageShow 中手动恢复状态。

Q:第一次授权相机成功,第二次失败?
A:检查是否在 JS 端重复调用了 navigator.mediaDevices.getUserMedia,且未在 onPermissionRequest 中正确处理授权逻辑。ArkWeb 要求每次请求都必须经过权限回调。

Q:runJavaScript 返回的 Promise 偶尔不 resolve?
A:通常是因为目标页面在执行 JS 时抛出了未被捕获的异常。建议在 JS 函数外层包裹 try-catch,并返回明确的 { success: true/false } 结构。

示例代码地址:项目地址

这篇文章是 ArkWeb 学习的起点,下一篇我们会深入讲解如何在一个实际电商项目中集成 ArkWeb,并处理权限、Cookie 同步和性能监控。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐