适配:HarmonyOS7,API26
连载:02/10

开篇:H5跳转不受控,是混合开发第二大坑

做鸿蒙ArkWeb混合应用,写完基础页面加载和生命周期之后,下一个一定会撞上的问题:H5里面点链接,直接在当前Web页面跳转。
想拦截某些链接,跳转到鸿蒙原生页面,或者禁止打开外部网页、拦截弹窗新窗口,默认行为根本做不到。

我之前接手一个项目,H5里面埋了外链。用户一点,直接跳外部网页,回退逻辑混乱;还有H5里window.open()打开新页面,直接失效,要么白屏,要么直接在当前页面覆盖,产品提了一堆bug。

很多新手以为H5的跳转只能交给前端控制。其实不是,HarmonyOS7的ArkWeb提供了跳转拦截回调,我们可以在原生层接管所有URL跳转请求,决定放行、拦截,甚至切到鸿蒙原生页面。

这篇手记,我们就解决路由接管这件事。学会拦截链接、区分内部H5路由和外部链接,处理H5新窗口打开场景,统一混合应用路由逻辑。

本篇目标:

  1. 理解ArkWeb的onUrlIntercept拦截回调触发时机
  2. 区分点击跳转、js跳转、window.open三种不同跳转事件
  3. 编写规则,放行内部业务链接,拦截外部域名
  4. H5 window.open新窗口劫持,用原生页面承载新Web实例
  5. 搭建简易路由管理器,H5链接一键跳转原生ArkUI页面

【IMAGE_01】

一、基础概念:ArkWeb跳转拦截到底拦截什么

先分清一件事:H5页面跳转分两类。
一类是用户点击a标签,触发页面跳转;另一类是JS代码执行location.href、window.open触发跳转。
在HarmonyOS7里,所有这类请求,都会经过onUrlIntercept回调。这个回调在页面发起请求之前触发,我们有机会直接决定要不要放行。

一句话类比:onUrlIntercept就像收费站。H5发起跳转请求,先到收费站,由原生代码判断:允许通行就继续加载网页;直接拦下,执行我们自定义逻辑。

常见误区
❌ 拦截回调只拦截a标签点击,JS跳转拦不住
✅ onUrlIntercept可以拦截绝大多数跳转,但是window.open属于单独事件,需要额外监听onNewWindow

HarmonyOS7路由拦截时序:

  1. H5触发跳转(点击链接 / JS修改location.href)
  2. 触发 onUrlIntercept,拿到目标URL
  3. 执行我们写的匹配规则
  4. 返回true:拦截,不再加载这个网页;返回false:放行,ArkWeb正常加载目标页面

划重点:返回值千万别搞反。很多人写反,导致页面点任何链接都被直接拦截,页面卡死。API26文档这点描述很容易看错,踩坑的人特别多。

在这里插入图片描述

二、最简代码:实现URL拦截,内外域名区分

先写基础拦截代码,给ArkWeb组件绑定onUrlIntercept。
我们做一个简单规则:本业务域名放行;外部第三方域名直接拦截,弹出提示。

import { Web, WebController, WebResourceError, WebUrlInterceptResult } from '@kit.ArkWeb';

@Entry
@Component
struct ArkWebRoutePage {
  private webController: WebController = new WebController();
  @State tipMsg: string = "";
  // 业务允许的H5域名白名单
  private allowHostList: string[] = ["xxx-business.com"];

  aboutToAppear() {
    this.webController.setJavaScriptEnabled(true);
    this.webController.setDomStorageEnabled(true);
  }

  build() {
    Column() {
      if(this.tipMsg.length > 0) {
        Text(this.tipMsg)
          .fontSize(16)
          .fontColor(Color.Orange)
          .margin(12)
      }
      Web({
        src: "https://xxx-business.com/index.html",
        controller: this.webController
      })
      .onUrlIntercept((url: string) => {
        console.info("捕获跳转URL:", url);
        const urlObj = new URL(url);
        const host = urlObj.hostname;
        // 判断是否在白名单
        const isAllow = this.allowHostList.some(item => host.includes(item));
        if(!isAllow) {
          this.tipMsg = "外部链接已拦截";
          // 返回true = 拦截本次跳转
          return WebUrlInterceptResult.INTERCEPT;
        }
        // 返回false = 放行,正常加载H5页面
        return WebUrlInterceptResult.ALLOW;
      })
      .width('100%')
      .layoutWeight(1)
    }
  }

  aboutToDisappear() {
    this.webController.stop();
    this.webController.destroy();
  }
}

代码逐段解释

  1. onUrlIntercept((url:string)=>{...}):跳转拦截回调,每次跳转都会把目标 URL 丢进来。
  2. new URL (url):解析域名、路径,方便做白名单匹配。注意,非法 URL 会抛出异常,生产环境需要加 try-catch。
  3. WebUrlInterceptResult.INTERCEPT:拦截跳转,不再加载页面;WebUrlInterceptResult.ALLOW:放行。

老版本教程会直接 return true/false,HarmonyOS7 API26 推荐使用枚举常量,可读性更强。

踩坑提醒:如果 H5 页面是本地 rawfile 静态页面,跳转的相对路径也会进入这个拦截回调,写匹配规则的时候要考虑相对路径场景,不要直接报错。

三、进阶场景:H5 window.open 新窗口打开处理

H5 页面经常有window.open("xxx.html"),用来弹出新页面。默认情况下,ArkWeb 不会自动新建页面,这个事件会触发onNewWindow回调。
onUrlIntercept 拦不住 window.open,很多人在这里卡很久。

我们绑定 onNewWindow 回调,当 H5 调用 window.open 的时候,由原生创建新 WebController,打开新的鸿蒙页面。

.onNewWindow((url:string) => {
  console.info("H5请求打开新窗口,地址:", url);
  // 这里可以调用路由,打开新页面,传入目标url
  // router.pushUrl打开新的ArkUI页面,新页面里面挂载独立Web组件
  return true;
})

返回 true 代表我们原生接管新窗口;返回 false,代表不支持新窗口,H5 的 window.open 直接失效。

这里要注意:每一个新窗口,都需要独立的 WebController 实例。不要复用当前页面的 controller,复用会导致两个页面互相抢占内核,出现渲染错乱。

【IMAGE_03】

四、混合路由:H5 链接直接跳转鸿蒙原生页面

这是混合开发非常实用的能力。约定一套自定义协议,比如harmony://native/pageDetail。
H5 点击链接,触发 onUrlIntercept,识别这个自定义协议,直接拦截,调用鸿蒙原生路由,打开原生页面,不加载任何网页。

修改拦截回调内部逻辑:

.onUrlIntercept((url: string) => {
  console.info("捕获跳转URL:", url);
  // 匹配自定义原生协议
  if(url.startsWith("harmony://native/")) {
    // 拦截,打开原生页面
    router.pushUrl({url: url});
    return WebUrlInterceptResult.INTERCEPT;
  }

  const urlObj = new URL(url);
  const host = urlObj.hostname;
  const isAllow = this.allowHostList.some(item => host.includes(item));
  if(!isAllow) {
    this.tipMsg = "外部链接已拦截";
    return WebUrlInterceptResult.INTERCEPT;
  }
  return WebUrlInterceptResult.ALLOW;
})

注意:需要导入 router 模块import router from '@ohos.router';
这种方案可以做到 H5 和原生路由统一管理。H5 不用关心原生页面怎么实现,只需要跳转约定协议链接。

五、异常兜底:URL 解析容错,防止页面崩溃

如果 H5 传了畸形、非法 URL,new URL(url)会直接抛出异常,整个页面直接崩溃。生产环境必须包一层 try catch。

.onUrlIntercept((url: string) => {
  try{
    console.info("捕获跳转URL:", url);
    if(url.startsWith("harmony://native/")) {
      router.pushUrl({url: url});
      return WebUrlInterceptResult.INTERCEPT;
    }
    const urlObj = new URL(url);
    const host = urlObj.hostname;
    const isAllow = this.allowHostList.some(item => host.includes(item));
    if(!isAllow) {
      this.tipMsg = "外部链接已拦截";
      return WebUrlInterceptResult.INTERCEPT;
    }
    return WebUrlInterceptResult.ALLOW;
  }catch(err) {
    console.error("URL解析失败", err);
    return WebUrlInterceptResult.INTERCEPT;
  }
})

六、真机调试,路由日志查看

模拟器测试跳转拦截逻辑,最好打开日志窗口。每次跳转,url 都会打印到控制台。
测试流程:

  1. H5 页面添加 a 标签,分别写内部业务链接、外部链接、自定义 native 协议链接
  2. 逐个点击,观察日志
  3. 检查:内部链接正常打开;外部链接拦截;native 协议跳转原生页面

小坑:部分 H5 框架路由是前端 hash 路由,# 后面的内容不会触发页面刷新,onUrlIntercept 不会触发。hash 路由属于前端内部状态变更,不会发起页面跳转请求,这点一定要分清。

在这里插入图片描述

七、本篇总结 + 课后练习任务

知识点汇总

  1. onUrlIntercept 在跳转发起前拦截请求,可以放行或者阻断网页加载;返回枚举不要写反。
  2. window.open 不会触发 onUrlIntercept,需要单独监听 onNewWindow 回调,新建独立 Web 实例。
  3. 自定义协议可以打通 H5 到原生页面跳转,实现混合应用路由统一。
  4. URL 解析一定要增加 try-catch,畸形 URL 会直接造成页面崩溃。
  5. hash 模式前端路由不会触发页面跳转拦截,这是前端路由本身的特性。

实践任务,为下一期做铺垫

基于本篇代码自行实现两点:

  1. 增加拦截黑名单域名列表,匹配关键词直接阻断;
  2. 监听页面回退事件,控制 Web 页面的 goBack 逻辑,和鸿蒙系统返回按键联动。

下一期手记 03,我们讲 ArkWeb 返回按键、历史栈管理,处理 H5 页面回退与原生页面回退冲突问题。
在这里插入图片描述

Logo

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

更多推荐