适配:HarmonyOS7,API26
连载:02/10
开篇:H5跳转不受控,是混合开发第二大坑
做鸿蒙ArkWeb混合应用,写完基础页面加载和生命周期之后,下一个一定会撞上的问题:H5里面点链接,直接在当前Web页面跳转。
想拦截某些链接,跳转到鸿蒙原生页面,或者禁止打开外部网页、拦截弹窗新窗口,默认行为根本做不到。
我之前接手一个项目,H5里面埋了外链。用户一点,直接跳外部网页,回退逻辑混乱;还有H5里window.open()打开新页面,直接失效,要么白屏,要么直接在当前页面覆盖,产品提了一堆bug。
很多新手以为H5的跳转只能交给前端控制。其实不是,HarmonyOS7的ArkWeb提供了跳转拦截回调,我们可以在原生层接管所有URL跳转请求,决定放行、拦截,甚至切到鸿蒙原生页面。
这篇手记,我们就解决路由接管这件事。学会拦截链接、区分内部H5路由和外部链接,处理H5新窗口打开场景,统一混合应用路由逻辑。
本篇目标:
- 理解ArkWeb的onUrlIntercept拦截回调触发时机
- 区分点击跳转、js跳转、window.open三种不同跳转事件
- 编写规则,放行内部业务链接,拦截外部域名
- H5 window.open新窗口劫持,用原生页面承载新Web实例
- 搭建简易路由管理器,H5链接一键跳转原生ArkUI页面
一、基础概念:ArkWeb跳转拦截到底拦截什么
先分清一件事:H5页面跳转分两类。
一类是用户点击a标签,触发页面跳转;另一类是JS代码执行location.href、window.open触发跳转。
在HarmonyOS7里,所有这类请求,都会经过onUrlIntercept回调。这个回调在页面发起请求之前触发,我们有机会直接决定要不要放行。
一句话类比:onUrlIntercept就像收费站。H5发起跳转请求,先到收费站,由原生代码判断:允许通行就继续加载网页;直接拦下,执行我们自定义逻辑。
常见误区
❌ 拦截回调只拦截a标签点击,JS跳转拦不住
✅ onUrlIntercept可以拦截绝大多数跳转,但是window.open属于单独事件,需要额外监听onNewWindow
HarmonyOS7路由拦截时序:
- H5触发跳转(点击链接 / JS修改location.href)
- 触发 onUrlIntercept,拿到目标URL
- 执行我们写的匹配规则
- 返回true:拦截,不再加载这个网页;返回false:放行,ArkWeb正常加载目标页面
划重点:返回值千万别搞反。很多人写反,导致页面点任何链接都被直接拦截,页面卡死。API26文档这点描述很容易看错,踩坑的人特别多。
二、最简代码:实现URL拦截,内外域名区分
先写基础拦截代码,给ArkWeb组件绑定onUrlIntercept。
我们做一个简单规则:本业务域名放行;外部第三方域名直接拦截,弹出提示。
import{Web,WebController,WebResourceError,WebUrlInterceptResult}from'@kit.ArkWeb';@Entry@Componentstruct ArkWebRoutePage{privatewebController:WebController=newWebController();@StatetipMsg:string="";// 业务允许的H5域名白名单privateallowHostList: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);consturlObj=newURL(url);consthost=urlObj.hostname;// 判断是否在白名单constisAllow=this.allowHostList.some(item=>host.includes(item));if(!isAllow){this.tipMsg="外部链接已拦截";// 返回true = 拦截本次跳转returnWebUrlInterceptResult.INTERCEPT;}// 返回false = 放行,正常加载H5页面returnWebUrlInterceptResult.ALLOW;}).width('100%').layoutWeight(1)}}aboutToDisappear(){this.webController.stop();this.webController.destroy();}}代码逐段解释
onUrlIntercept((url:string)=>{...}):跳转拦截回调,每次跳转都会把目标 URL 丢进来。- new URL (url):解析域名、路径,方便做白名单匹配。注意,非法 URL 会抛出异常,生产环境需要加 try-catch。
- 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,复用会导致两个页面互相抢占内核,出现渲染错乱。
四、混合路由:H5 链接直接跳转鸿蒙原生页面
这是混合开发非常实用的能力。约定一套自定义协议,比如harmony://native/pageDetail。
H5 点击链接,触发 onUrlIntercept,识别这个自定义协议,直接拦截,调用鸿蒙原生路由,打开原生页面,不加载任何网页。
修改拦截回调内部逻辑:
.onUrlIntercept((url:string)=>{console.info("捕获跳转URL:",url);// 匹配自定义原生协议if(url.startsWith("harmony://native/")){// 拦截,打开原生页面router.pushUrl({url:url});returnWebUrlInterceptResult.INTERCEPT;}consturlObj=newURL(url);consthost=urlObj.hostname;constisAllow=this.allowHostList.some(item=>host.includes(item));if(!isAllow){this.tipMsg="外部链接已拦截";returnWebUrlInterceptResult.INTERCEPT;}returnWebUrlInterceptResult.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});returnWebUrlInterceptResult.INTERCEPT;}consturlObj=newURL(url);consthost=urlObj.hostname;constisAllow=this.allowHostList.some(item=>host.includes(item));if(!isAllow){this.tipMsg="外部链接已拦截";returnWebUrlInterceptResult.INTERCEPT;}returnWebUrlInterceptResult.ALLOW;}catch(err){console.error("URL解析失败",err);returnWebUrlInterceptResult.INTERCEPT;}})六、真机调试,路由日志查看
模拟器测试跳转拦截逻辑,最好打开日志窗口。每次跳转,url 都会打印到控制台。
测试流程:
- H5 页面添加 a 标签,分别写内部业务链接、外部链接、自定义 native 协议链接
- 逐个点击,观察日志
- 检查:内部链接正常打开;外部链接拦截;native 协议跳转原生页面
小坑:部分 H5 框架路由是前端 hash 路由,# 后面的内容不会触发页面刷新,onUrlIntercept 不会触发。hash 路由属于前端内部状态变更,不会发起页面跳转请求,这点一定要分清。
七、本篇总结 + 课后练习任务
知识点汇总
- onUrlIntercept 在跳转发起前拦截请求,可以放行或者阻断网页加载;返回枚举不要写反。
- window.open 不会触发 onUrlIntercept,需要单独监听 onNewWindow 回调,新建独立 Web 实例。
- 自定义协议可以打通 H5 到原生页面跳转,实现混合应用路由统一。
- URL 解析一定要增加 try-catch,畸形 URL 会直接造成页面崩溃。
- hash 模式前端路由不会触发页面跳转拦截,这是前端路由本身的特性。
实践任务,为下一期做铺垫
基于本篇代码自行实现两点:
- 增加拦截黑名单域名列表,匹配关键词直接阻断;
- 监听页面回退事件,控制 Web 页面的 goBack 逻辑,和鸿蒙系统返回按键联动。
下一期手记 03,我们讲 ArkWeb 返回按键、历史栈管理,处理 H5 页面回退与原生页面回退冲突问题。