HarmonyOS ArkWeb 开发完整指南(下篇):同层渲染与 Web 拦截
本文是下篇,主要介绍组件鸿蒙化(同层渲染)、基于 Web 的视频适配、网页跨域解决方案和 Web 组件拦截能力。上篇已介绍 Hybrid 应用鸿蒙化方案、双端通信(JSBridge)和 API 鸿蒙化。
组件鸿蒙化(同层渲染)
HarmonyOS 提供同层渲染能力把原生组件直接渲染到 WebView 层级,从而获得更大的灵活性以及性能上获得更好表现。
同层渲染原理
开发者可通过 Web 组件同层渲染相关属性来进行控制:
- enableNativeEmbedMode:开关控制
- onNativeEmbedLifecycleChange:处理同层渲染生命周期(CREATE/UPDATE/DESTROY)
- onNativeEmbedGestureEvent:处理交互事件
同层渲染功能要求前端页面文件中显式使用 embed 标签,并且 embed 标签内 type 必须以"native/"开头。
使用 Vue 等框架可以方便地进一步封装 embed 标签生成自定义组件,并增加更多属性、事件和方法,通过 JSBridge 与 ArkTS 侧进行同步。
在 ArkTS 侧,对应地需要自定义实现一个原生组件或者使用系统内置组件,通过 NodeContainer 组件进行动态挂载。
离屏节点动态上下树
- 开发者初始构建一个 NodeContainer 对象表示一个空的占位符。NodeContainer 里面内容为空时,在初始化的时候大小为 0,不参与布局。
- NodeController 持有 buildnode 对象,通过 makeNode() 接口将 buildnode 对象返回给 NodeContainer,来实现动态上树。
- NodeController 里面 rebuild() 方法,触发 NodeContainer 重新调用 makeNode() 接口。makeNode() 接口若返回空,则实现动态下树。
前端使用示例
使用 H5 结合 embed 标签示例:
<div><divid="bodyId"><embedid="nativeSearch"type="native/component"width="100%"height="100%"src="view"/></div></div>ArkTS 侧实现
在 ArkTS 侧,可以扩展 NodeController 来统一管理同层渲染节点。其 makeNode() 接口实现示例如下:
classSearchNodeControllerextendsNodeController{private rootNode: BuilderNode<[Params]>|undefined|null=null;private embedId:string="";private surfaceId:string="";private renderType: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;private componentWidth:number=0;private componentHeight:number=0;private componentType:string="";setRenderOption(params: NodeControllerParams):void{this.surfaceId = params.surfaceId;this.renderType = params.renderType;this.embedId = params.embedId;this.componentWidth = params.width;this.componentHeight = params.height;this.componentType = params.type;}makeNode(uiContext: UIContext): FrameNode |null{this.rootNode =newBuilderNode(uiContext,{ surfaceId:this.surfaceId, type:this.renderType });if(this.componentType ==='native/component'){this.rootNode.build(wrapBuilder(searchBuilder),{ width:this.componentWidth, height:this.componentHeight });}returnthis.rootNode.getFrameNode();}getBuilderNode(): BuilderNode<[Params]>|undefined|null{returnthis.rootNode;}updateNode(arg: Object):void{this.rootNode?.update(arg);}getEmbedId():string{returnthis.embedId;}postEvent(event: TouchEvent |undefined):boolean{returnthis.rootNode?.postTouchEvent(event)asboolean;}}基于 Web 的视频适配
在 Hybrid 应用中,视频播放是一个常见且性能敏感的场景。
视频适配方案
基于 Web 的视频适配主要考虑以下几个方面:
- 视频播放器选择:使用系统原生播放器还是 H5 播放器
- 同层渲染:使用同层渲染能力将原生播放器渲染到 WebView 层级
- 全屏适配:处理视频全屏播放时的适配问题
- 手势交互:处理视频播放的手势操作(播放、暂停、进度调节等)
实现要点
- 使用 embed 标签嵌入原生视频播放器组件
- 通过 JSBridge 实现前端与原生播放器的通信
- 处理视频播放的生命周期(创建、更新、销毁)
- 适配不同屏幕尺寸和方向
网页跨域解决方案
在 Hybrid 应用开发中,跨域问题是一个常见的问题。
跨域问题场景
- H5 页面请求后端接口跨域
- iframe 嵌套跨域页面
- 访问第三方服务跨域
解决方案
方案一:WebSchemeHandler 拦截
使用 WebSchemeHandler 机制拦截跨域请求,转发至远端服务器,将跨域请求结果配置到自定义响应中,返回给 Web 组件。
// 设置 WebSchemeHandler controller.setWebSchemeHandler('http',this.schemeHandler);// 在 onRequestStart 回调中拦截网络请求this.schemeHandler.onRequestStart((request: webview.WebSchemeHandlerRequest, resourceHandler: webview.WebResourceHandler)=>{// 处理请求const handled =this.model.processRequest(request, resourceHandler);return handled;});方案二:配置公共请求头
在网络访问的过程中,在请求头中携带认证信息,使服务端能够识别用户身份并验证其访问权限。
privatecreateSession(headers: Record<string,string>):void{try{const sessionConfig: rcp.SessionConfiguration ={ headers: headers };this.session = rcp.createSession(sessionConfig);}catch(error){// ...}}方案三:后端配置 CORS
在服务器端配置 CORS(Cross-Origin Resource Sharing),允许特定来源的跨域请求。
Web 组件拦截能力
ArkWeb(方舟 Web)提供的 Web 组件支持在应用嵌入网页访问功能。
在实际使用过程中,网页内的大量图片、视频等静态资源容易造成用户流量消耗过快,同时,嵌入的恶意脚本也会拖慢网页加载速度,影响用户体验。
为此,可以利用 Web 组件的拦截能力,使用本地资源副本替换高频访问的静态资源,并基于黑名单机制拦截恶意脚本的加载,从而有效提升网页加载效率,优化用户体验。
三种拦截方案
ArkWeb 提供了多种拦截能力,使开发者能够监控、修改和记录网络请求及其响应。
这些能力在拦截时机、拦截范围和拦截效果等方面存在差异,因此适用于不同的开发场景。
| 方案 | onLoadIntercept | onInterceptRequest | WebSchemeHandler |
|---|---|---|---|
| 使用场景 | 页面跳转控制 | 网络请求拦截 | 网络请求拦截 |
| 典型应用场景 | 应用的跳转与拉起、请求重定向、页面白名单配置 | 本地资源替换、自定义资源加载策略、提示恶意请求 | 除 onInterceptRequest 场景外,还支持配置公共请求头、跨域请求、POST 请求拦截 |
| 拦截时机 | Web 组件加载 url 之前 | 请求发起前 | 请求发起前 |
| 拦截范围 | 页面主 URL 的请求(包括页面中 iframe 的导航行为,不包括子资源的请求) | 页面主 URL 的请求和子资源的请求 | 页面主 URL 的请求和子资源的请求 |
| 数据访问能力 | 支持获取请求的 URL、是否为主 frame 等相关信息 | 除支持获取请求的 URL、是否为主 frame 等相关信息外,还支持获取 POST 请求体和 buffer 类型数据 | 支持获取完整的请求信息 |
| 拦截效果 | 可以拦截或放行 Web 组件发起当前请求 | 可以拦截当前请求并返回自定义响应,或者放行当前请求并返回原始响应 | 可以拦截当前请求并返回自定义响应,支持流式处理 |
方案选择指导
onLoadIntercept
定位:用于拦截 Web 组件的 URL 加载行为,进行页面跳转控制。
核心用法:
- 应用的跳转与拉起:拦截特定地址的请求,拉起指定应用或跳转其他页面处理,用于实现拦截支付类标签链接跳转到支付应用进行支付,或拦截地址类标签链接跳转到地图类应用进行导航等。
- 请求重定向:拦截特定地址的请求,并将访问重定向到新的目标地址,用于在域名更换或登录引导时,将用户访问跳转到正确的页面。
- 页面白名单配置:配置链接黑名单/白名单,拦截/放行指定 URL 请求,确保 Web 组件的请求在信任范围内,用于实现阻止用户访问危险网页等功能。
使用策略:相较于其他两种拦截方案,onLoadIntercept 的主要目标是拦截页面跳转行为,而非替换请求内容。因此,在需要中断页面跳转的情况下,可选择使用本方案。
onInterceptRequest
定位:用于拦截 Web 组件的 URL 加载行为,进行网络请求拦截。
核心用法:
- 本地资源替换:将频繁使用的静态资源缓存至本地,在访问这些资源时,返回本地响应,用于提升页面的加载速度和响应性能。
- 自定义资源加载策略:拦截特定资源请求(如图片、视频等),在 Wi-Fi 和移动网络下分别加载高清资源和压缩资源,用于实现数据消耗与用户体验的平衡。
- 提示恶意请求:拦截已知恶意请求,返回空数据或提示信息作为请求响应,用于阻止恶意脚本的加载。
使用策略:onInterceptRequest 支持通过文件句柄、Resource 资源、ByteBuffer 或 String 的方式替换请求内容,要求开发者一次性提供完整的请求内容。在需要拦截网络请求并向 Web 组件返回特定响应的业务场景下,可选择本方案。相较于基于 WebSchemeHandler 的拦截方案,本方案的实现更加轻量。
WebSchemeHandler
定位:用于拦截 Web 组件的 URL 加载行为,进行网络请求拦截。
核心用法:
除支持 onInterceptRequest 的核心用法外,还支持:
- 配置公共请求头:拦截特定地址的网络请求,注入认证信息后转发给服务端,用于协助服务端识别请求的用户身份并验证其访问权限。
- 跨域请求:拦截 Web 组件发起的跨域请求,转发至远端服务器,将跨域请求结果配置到自定义响应中,返回给 Web 组件,用于解决依赖多元数据交互的 Web 应用的跨域问题。
- POST 请求拦截:拦截 POST 请求,根据请求内容动态生成响应,用于实现表单提交处理等功能。
使用策略:相较于基于 onInterceptRequest 的拦截方案,WebSchemeHandler 不仅具备其全部功能,还提供了更灵活的流式处理能力,允许开发者通过 ByteBuffer 逐步提供请求内容,并且能够获取请求的上传内容,如 POST 请求的数据。在需要拦截网络请求,获取更多请求内容或进行流式处理等复杂业务场景下,建议采用本方案。
基于 onLoadIntercept() 拦截能力的使用
Web 组件在加载 URL 前会触发 onLoadIntercept() 回调,用于判断是否拦截此次请求。基于该回调,可以实现请求重定向或页面白名单配置功能。
请求重定向
请求重定向的典型应用是在网站改版或登录状态管理等场景中,将用户访问自动跳转到正确的页面。
实现原理:
在 Web 组件的 onLoadIntercept() 回调中获取请求的 URL,若 URL 满足重定向条件,则通过 WebviewController.loadUrl() 加载重定向页面。
开发步骤:
- 获取请求的 URL
- 判断 URL 是否满足重定向条件
- 若满足重定向条件,通过 WebviewController.loadUrl() 加载重定向页面
processLoadIntercept(event: OnLoadInterceptEvent):boolean{const requestUrl = event.data.getRequestUrl();if(this.shouldInterceptUrl(requestUrl)){const redirected =this.performRedirect();return redirected;}returnfalse;}shouldInterceptUrl(requestUrl:string):boolean{if(!this.redirectUrl){returnfalse;}const normalizedRequest =this.normalizeUrl(requestUrl);const normalizedRedirect =this.normalizeUrl(this.redirectUrl);const isRedirectTarget = normalizedRequest === normalizedRedirect;return!isRedirectTarget;}performRedirect():boolean{try{this.controller.loadUrl(this.redirectUrl);returntrue;}catch(error){returnfalse;}}页面白名单配置
页面白名单配置的典型应用是通过限制访问来源,仅允许可信来源接入系统,来防止非授权访问和网络攻击。
实现原理:
在 Web 组件的 onLoadIntercept() 回调中获取请求的 URL,若 URL 不属于白名单链接,则跳转到浏览器打开请求页面。
开发步骤:
- 配置页面白名单链接
- 获取请求的 URL
- 判断 URL 是否属于白名单链接
- 若不属于白名单链接,通过弹窗提示是否跳转到浏览器打开
processLoadIntercept(event: OnLoadInterceptEvent):boolean{const requestUrl = event.data.getRequestUrl();if(this.isUrlInWhitelist(requestUrl)){this.allowAllForCurrentLoad =true;returnfalse;}this.showDialog(requestUrl);returntrue;}isUrlInWhitelist(requestUrl:string):boolean{const requestDomain =this.extractDomain(requestUrl);if(!requestDomain){returnfalse;}returnthis.whitelistDomains.includes(requestDomain);}基于 onInterceptRequest() 拦截能力的使用
Web 组件在加载 URL 之前会触发 onInterceptRequest() 回调,用于判断是否拦截此次请求并返回自定义响应数据。
基于该回调,可以实现本地资源替换或自定义资源加载策略。
本地资源替换
本地资源替换的典型应用是将部分频繁使用且变动较小的远程静态资源缓存至本地,以提升页面的加载速度和响应性能。
实现原理:
在 Web 组件的 onInterceptRequest() 回调中获取网络请求信息,通过网络请求资源与本地资源的映射关系,获取对应的本地资源并将其设置给 WebResourceResponse 作为请求响应。
开发步骤:
- 配置网络请求资源和本地资源的映射关系,以及本地资源与相应 MIME 类型的映射关系
- 获取请求的 URL
- 通过映射关系获取本地资源
- 构建 WebResourceResponse,设置并返回本地资源作为请求响应
// Map between domain names and local files schemeMap =newMap([['https://www.example.com/','index.html'],['https://www.example.com/mountain.png','mountain.png']]);// Map between local files and format mimeType mimeTypeMap =newMap([['index.html','text/html'],['mountain.png','image/png']]);processRequest(event: OnInterceptRequestEvent |null): WebResourceResponse |null{const requestUrl = event.request.getRequestUrl();const key =this.getUrlSchemeFromMap(requestUrl);if(key.length ===0){returnnull;}const rawfileName =this.schemeMap.get(key);if(!rawfileName){returnnull;}const mimeType =this.mimeTypeMap.get(rawfileName);if(!mimeType){returnnull;}returnthis.createLocalResourceResponse(rawfileName, mimeType);}自定义资源加载策略
自定义资源加载策略的典型应用是在 Wi-Fi 网络环境下加载高清图片,而在非 Wi-Fi 网络环境下加载压缩图片或本地占位图,以实现数据消耗与体验优化的平衡。
实现原理:
在 Web 组件的 onInterceptRequest() 回调中获取网络请求信息,对于图片资源请求,判断当前是否处于 Wi-Fi 网络环境下。若非 Wi-Fi 网络环境,则将本地占位图设置给 WebResourceResponse 作为请求响应。
开发步骤:
- 判断当前网络环境,若处于 Wi-Fi 网络环境下,则直接返回原始请求响应
- 获取请求的 URL
- 判断请求类型,若非图片资源请求,则直接返回原始请求响应
- 构建 WebResourceResponse,对于非 Wi-fi 网络环境下的图片资源请求,设置并返回本地占位图作为请求响应
processRequest(event: OnInterceptRequestEvent |null): WebResourceResponse |null{if(this.isWifiNetwork()){returnnull;}const requestUrl = event.request.getRequestUrl();if(!this.isImageRequestUrl(requestUrl)){returnnull;}returnthis.createPlaceholderResponse();}isWifiNetwork():boolean{try{const netHandle = connection.getDefaultNetSync();const netData = connection.getNetCapabilitiesSync(netHandle);return netData.bearerTypes.includes(connection.NetBearType.BEARER_WIFI);}catch(error){returnfalse;}}基于 WebSchemeHandler 拦截能力的使用
为当前 Web 组件设置 WebSchemeHandler,可以拦截指定协议的请求,获得请求信息并返回自定义响应数据。
基于 WebSchemeHandler 机制,可以实现配置公共请求头等场景。
配置公共请求头
配置公共请求头的典型应用是在网络访问的过程中,在请求头中携带认证信息,使服务端能够识别用户身份并验证其访问权限。
实现原理:
通过 WebviewController 将 WebSchemeHandler 设置给当前 Web 组件后,在 WebSchemeHandler.onRequestStart() 回调中拦截网络请求,并为其添加公共请求头,然后通过 rcp 将请求转发到服务端。
开发步骤:
- 通过 WebviewController 将 WebSchemeHandler 设置给 Web 组件
- 在 WebSchemeHandler.onRequestStart() 回调中拦截网络请求
- 为网络请求添加自定义的公共请求头,并通过 rcp.createSession() 创建 HTTP 会话
- 通过 rcp 将请求转发到服务端获取请求响应
- 调用 didReceiveResponse() 和 didReceiveResponseBody() 将构造的响应头和响应体传递给拦截的请求
- 调用 didFinish() 通知 Web 组件被拦截的请求已经完成
// Bind interceptor to HTTP controller.setWebSchemeHandler('http',this.schemeHandler);// Set up request interceptorthis.schemeHandler.onRequestStart((request: webview.WebSchemeHandlerRequest, resourceHandler: webview.WebResourceHandler)=>{const handled =this.model.processRequest(request, resourceHandler);return handled;});privatecreateSession(headers: Record<string,string>):void{try{const sessionConfig: rcp.SessionConfiguration ={ headers: headers };this.session = rcp.createSession(sessionConfig);}catch(error){// ...}}privatehandleResponse( response: rcp.Response, resourceHandler: webview.WebResourceHandler,):void{try{const webResponse =newwebview.WebSchemeHandlerResponse(); webResponse.setStatus(response.statusCode ||200); webResponse.setStatusText('OK'); webResponse.setMimeType(mimeType); webResponse.setEncoding(encoding); webResponse.setNetErrorCode(WebNetErrorList.NET_OK);// Set CORS headers webResponse.setHeaderByName('Access-Control-Allow-Origin','*',true); webResponse.setHeaderByName('Access-Control-Allow-Credentials','true',true); webResponse.setHeaderByName('Access-Control-Allow-Methods','GET, POST, PUT, DELETE, PATCH, OPTIONS',true); webResponse.setHeaderByName('Access-Control-Allow-Headers','Content-Type, Authorization, X-Custom-Header',true); resourceHandler.didReceiveResponse(webResponse); resourceHandler.didReceiveResponseBody(response.body); resourceHandler.didFinish();}catch(error){// ...}}常见问题
Web 组件是否支持拦截前端页面的 router.push() 方法?
不支持。Web 组件目前仅提供网络请求的拦截方法,而前端页面的 router.push() 方法不会触发新的网络请求,因此无法被拦截。
Web 组件支持异步判断是否拦截网络请求吗?
不支持。Web 组件不支持异步判断是否拦截网络请求,但是可以在拦截网络请求后,异步处理响应数据。
Web 组件是否支持拦截 Ajax 原始响应?
不支持。Web 组件目前仅提供网络请求的拦截方法,无法拦截请求的响应。然而,可以通过 onInterceptRequest() 回调或 WebSchemeHandler 机制自定义响应。
下篇总结
下篇介绍了组件鸿蒙化和 Web 拦截能力:
组件鸿蒙化(同层渲染)
- 同层渲染原理(enableNativeEmbedMode、生命周期、交互事件)
- 离屏节点动态上下树
- 前端使用示例(embed 标签)
- ArkTS 侧实现(NodeController 扩展)
基于 Web 的视频适配
- 视频播放器选择
- 同层渲染应用
- 全屏适配
- 手势交互
网页跨域解决方案
- WebSchemeHandler 拦截
- 配置公共请求头
- 后端配置 CORS
Web 组件拦截能力
- 三种拦截方案对比(onLoadIntercept/onInterceptRequest/WebSchemeHandler)
- 请求重定向实现
- 页面白名单配置
- 本地资源替换
- 自定义资源加载策略
- 配置公共请求头
全文总结
这篇文章介绍了 ArkWeb 开发的几个核心话题:
Hybrid 应用鸿蒙化方案
- 整体架构:Ark 进程、Webview 进程、JSBridge
- 双端通信:JavaScriptProxy 代理机制、分层设计(通信层/通道层/方法层)
- API 鸿蒙化:三种规范格式(func/on-offFunc/getXxManager)
- 组件鸿蒙化:同层渲染能力、embed 标签、NodeContainer 动态挂载
同层渲染 Web
- 使用 enableNativeEmbedMode 开关控制
- 处理生命周期(CREATE/UPDATE/DESTROY)
- 处理交互事件
- 离屏节点动态上下树
网页跨域解决方案
- WebSchemeHandler 拦截
- 配置公共请求头
- 后端配置 CORS
Web 组件拦截能力
- onLoadIntercept:页面跳转控制(重定向、白名单配置)
- onInterceptRequest:网络请求拦截(本地资源替换、自定义资源加载策略)
- WebSchemeHandler:网络请求拦截(配置公共请求头、跨域请求、POST 请求拦截)
核心思想:根据业务场景选择合适的拦截方案,利用同层渲染能力提升性能,通过 JSBridge 实现双端通信。