《HarmonyOS技术精讲-ArkWeb》快速上手:嵌入你的第一个Web页面

在这里插入图片描述

为什么需要ArkWeb

HarmonyOS NEXT应用开发中,Web页面加载是一个高频需求。最常见的场景包括:在APP内打开活动页面、加载第三方H5业务、内嵌后台管理系统的移动端视图。

直接使用系统内置浏览器?用户会跳出应用,体验割裂。自己写一个浏览器内核?不现实。

ArkWeb就是HarmonyOS官方提供的Web组件能力,它不是一个独立的浏览器应用,而是可以直接嵌入到你ArkUI页面里的Web组件。它基于Chromium内核,支持H5标准能力,性能在HarmonyOS NEXT上做了针对性优化。

不过,很多人在第一次使用ArkWeb时,会遇到几个实际痛点:

  1. 组件声明:官方文档只说了一个Web标签,但放在不同生命周期里行为差异很大。
  2. 页面加载控制:如何精确控制加载进度、捕获加载失败、处理页面跳转?默认行为往往不够用。
  3. 配置灵活性:缩放、滚动、缓存这些配置项,直接使用默认值,在真机上表现和模拟器不一致。

这篇文章就围绕这些问题,从零开始构建一个完整的ArkWeb页面,把每个关键点和坑都讲清楚。

环境说明

在开始编码之前,确保你的开发环境满足以下条件:

  • DevEco Studio 版本:DevEco Studio 6.1.0 及以上
  • HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上
  • 目标设备:手机(推荐真机调试)

核心实现:一个完整的Web页面

第一步:创建基础页面结构

我们先创建一个最简单的ArkUI页面,用于嵌入Web组件。打开pages/Index.ets,写入以下代码:

// 这段代码用于声明一个带有Web组件的页面
@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        // 声明一个Web组件,加载百度首页
        Web({ src: 'https://www.baidu.com', controller: new WebviewController() })
          .width('100%')
          .height('100%')
      }
      .width('100%')
      .height('100%')
    }
  }
}

这段代码会直接加载百度首页。看起来很简单,但这里有一个潜在问题:WebviewController 在组件每次重建时都会创建一个新实例,这会导致Web页面状态丢失。

第二步:关联状态管理与生命周期

为了解决状态丢失问题,我们需要把WebviewController和页面生命周期绑定。下面是一个更完整的写法:

@Entry
@Component
struct Index {
  // 将WebviewController定义为@State,确保组件重建时能保持引用
  @State private webController: WebviewController = new WebviewController();

  build() {
    Row() {
      Column() {
        Web({ src: 'https://www.baidu.com', controller: this.webController })
          .width('100%')
          .height('100%')
          // 页面加载开始回调
          .onPageBegin((event) => {
            console.info('Web页面开始加载:', event.url);
          })
          // 页面加载结束回调
          .onPageEnd((event) => {
            console.info('Web页面加载完成:', event.url);
          })
          // 页面加载错误回调
          .onErrorReceive((event) => {
            console.error('Web页面加载错误:', event.error.getErrorInfo());
          })
      }
      .width('100%')
      .height('100%')
    }
    .width('100%')
    .height('100%')
  }
}

这里重点说几个关键点:

  • WebviewController 放在 @State 里管理。很多初学者会把 controller 定义成普通变量,结果页面切换回来之后Web页面空白。
  • onPageBegin / onPageEnd / onErrorReceive 是三个最常用的生命周期回调,直接绑定在Web组件上。
  • 这些回调在build()方法中链式调用,代码结构更清晰。

第三步:加载本地HTML文件

除了远程URL,ArkWeb也支持加载本地HTML。把src属性改成 $rawfile 路径即可:

Web({ src: $rawfile('web/index.html'), controller: this.webController })

注意:

  • $rawfile 指向的资源目录是 resources/rawfile
  • 文件夹名称必须全部小写,否则在HarmonyOS上会报资源找不到的错误。
  • 本地HTML中的JS、CSS、图片资源必须使用相对路径,或者通过$rawfile引用。不支持绝对路径。

第四步:控制缩放与滚动

很多H5页面默认支持缩放,但嵌入到APP里后,用户双指缩放往往会导致页面错乱。我们需要精确控制:

Web({ src: 'https://www.baidu.com', controller: this.webController })
  .width('100%')
  .height('100%')
  // 禁止用户手动缩放
  .zoomAccess(false)
  // 允许JS控制缩放(部分页面需要)
  // .javascriptAccess(true)
  // 设置视口宽度为设备宽度(推荐)
  .width('100%')
  .height('100%')

实际场景分析:

  • 如果页面自带自适应缩放逻辑(如响应式布局),推荐同时开启javascriptAccess(true)zoomAccess(false),让JS控制缩放。
  • 如果是老旧页面(固定宽度),则需要开启zoomAccess(true),并且配合zoomFactor调整初始缩放比例。但手动缩放后页面布局大概率会乱,所以不推荐。

踩坑记录

坑1:页面返回后Web组件白屏

现象:进入二级页面,然后返回首页,首页的Web组件变成空白。

原因:页面进入AboutToDisappear生命周期时,ArkWeb组件会被销毁。如果你没有保存WebviewController的状态,或者没有正确重建Web组件,就会白屏。

解决方案:主要有两种方式:

  1. 管理WebviewController的生命周期:把WebviewController定义在页面级别的状态管理中(如上文的@State),并且不要在页面销毁时主动释放它。
  2. 使用页面路由栈:如果页面层级复杂,推荐使用@Consume或AppStorage存储WebviewController的引用。
@Entry
@Component
struct Index {
  @State private webController: WebviewController = new WebviewController();

  // 页面即将显示时,重新加载页面(如果需要)
  aboutToAppear() {
    // 如果之前有缓存,这里可以不处理
  }

  // 页面即将消失时,保存状态
  aboutToDisappear() {
    // 通常不需要显式保存ArkWeb内部状态,但可以保存当前URL
  }
}

坑2:加载失败不显示错误提示

现象:网络断开时,Web组件直接显示一个空白页面,没有给用户任何反馈。

原因onErrorReceive 只是回调函数,并不会自动显示错误页面。

解决方案:在onErrorReceive中手动加载一个本地错误页面:

Web({ src: 'https://www.baidu.com', controller: this.webController })
  .onErrorReceive((event) => {
    console.error('页面加载错误:', event.error.getErrorInfo());
    // 加载本地错误提示页面
    this.webController.loadUrl($rawfile('error/network_error.html'));
  })

坑3:滚动到顶部/底部时触发页面返回

现象:用户在Web页面内向上滚动,已经到达顶部,手指继续上滑,App意外返回上一页。

原因:这是HarmonyOS默认的右滑返回手势导致的。当Web组件的滚动容器滚动到边界(顶部或底部)时,手势会被传递到页面路由。

解决方案:在Web组件上设置scrollEnabled属性,并监听滚动事件来动态控制手势策略:

@State private scrollPosition: number = 0;
@State private isAtTop: boolean = true;

// 在build中
Web({...})
  .scrollEnabled(true)
  .onScroll((event) => {
    this.scrollPosition = event.scrollY;
    this.isAtTop = this.scrollPosition <= 0;
  })

然后在页面路由层判断isAtTop,当滚动到顶部时禁用返回手势。这个场景在列表类页面中比较常见。

最佳实践

  1. 不要滥用@State在Web组件内@State和Web组件内嵌的JS状态是两套体系。不要尝试在ArkTS中直接监控Web页面的DOM变化,正确做法是通过JSBridge通信。

  2. 预加载WebviewController:如果页面第一个渲染的就是Web组件,且URL是已知的,可以在aboutToAppear中提前初始化WebviewController,减少用户等待时间。

  3. 按需求部署缓存策略:如果加载的页面是固定内容,可以使用cacheMode属性设置为CacheMode.CacheEnabled,ArkWeb会自动使用缓存,减少网络请求。但如果是实时数据页面,记得关掉缓存,或者设置短缓存周期。

FAQ

Q:为什么真机正常,模拟器加载慢或者不显示?

A:模拟器网络环境不稳定是主要原因。建议开发阶段直接用真机调试。另外模拟器对本地$rawfile路径的大小写敏感,容易报资源找不到的错误。

Q:为什么页面返回后Web状态丢失,比如登录状态被清空?

A:ArkWeb组件在页面销毁时会清空WebviewController内部的缓存,包括Cookie和Session。如果需要在页面切换后保持登录状态,需要手动保存Cookie到本地存储,并在页面回到前台时恢复。

Q:Web组件可以加载HTTPS页面吗?

A:可以。但如果是自签名证书或不受信任的证书,默认会被拒绝。需要在webConfiguration中设置credentialsUse,但安全风险较高,生产环境不建议这么做。


示例代码地址:项目地址

总结一下:ArkWeb嵌入Web页面并不复杂,核心在于处理好WebviewController的生命周期和状态管理。@State管理控制器、监听onPageBegin/onEnd控制加载状态、onErrorReceive处理异常、zoomAccess控制缩放,这几个点掌握好,基本能覆盖90%的业务场景。

如果你在项目里遇到其他坑,欢迎交流。

Logo

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

更多推荐