鸿蒙6.0应用开发——Web组件的生命周期
开发者可以使用Web组件加载本地或者在线网页。Web组件提供生命周期回调接口,用于感知状态变化和处理业务。Web组件的状态主要包括:Controller绑定到Web组件、网页加载开始、网页加载进度、网页加载结束、页面即将可见。Web页面保活可以参考使用离线Web组件。
鸿蒙6.0应用开发——Web组件的生命周期
文章目录
概述
开发者可以使用Web组件加载本地或者在线网页。
Web组件提供生命周期回调接口,用于感知状态变化和处理业务。
Web组件的状态主要包括:Controller绑定到Web组件、网页加载开始、网页加载进度、网页加载结束、页面即将可见。
Web页面保活可以参考使用离线Web组件。
自定义组件析构销毁时执行aboutToDisappear函数,Web组件会被销毁,Web组件与WebviewController解绑,js运行环境也会一并销毁。

Web组件网页正常加载过程所涉及的状态说明
- aboutToAppear函数:在创建自定义组件的新实例后,在执行其build函数前执行。建议在此设置WebDebug调试模式、自定义协议URL的权限、Cookie等。
- onControllerAttached事件:当Controller成功绑定到Web组件时触发该回调,且禁止在该事件回调前调用Web组件相关的接口,否则会抛出js-error异常。建议在此事件中注入JS对象、设置自定义用户代理,使用操作网页不相关的接口。但因为该回调调用时网页还未加载,因此无法在回调中使用有关操作网页的接口,例如zoomIn、zoomOut等。
- onLoadIntercept事件:当Web组件加载url之前触发该回调,用于判断是否阻止此次访问。默认允许加载。
- onInterceptRequest事件:当Web组件加载url之前触发该回调,用于拦截url并返回响应数据。
- onPageBegin事件:网页开始加载时触发该回调,且只在主frame(表示一个用于展示HTML页面的元素)触发。如果是iframe或者frameset(用于包含frame的HTML标签)的内容加载时则不会触发此回调。多frame页面可能同时加载,主frame加载结束时子frame可能仍在加载。同一页面导航或失败的导航不会触发该回调。
- onProgressChange事件:告知开发者当前页面加载的进度。多frame页面或者子frame可能还在继续加载而主frame已经加载结束,所以在onPageEnd事件后仍可能收到该事件。
- onPageEnd事件:网页加载完成时触发该回调,且只在主frame触发。多frame页面有可能同时开始加载,即使主frame已经加载结束,子frame也有可能才开始或者继续加载中。同一页面导航或失败的导航不会触发该回调。建议在此回调中执行JavaScript脚本。注意,收到该回调不能保证下一帧反映DOM状态。
Web组件网页异常加载过程所涉及的状态说明
-
onOverrideUrlLoading事件:当URL将要加载到当前Web中时,让宿主应用程序有机会获得控制权,回调函数返回true将导致当前Web中止加载URL,而返回false则会导致Web继续照常加载URL。onLoadIntercept接口和onOverrideUrlLoading接口行为不一致,触发时机也不同,所以在应用场景上存在一定区别。onLoadIntercept事件在LoadUrl和iframe加载时触发,但onOverrideUrlLoading事件在LoadUrl和特定iframe加载时不会触发。
-
onPageVisible事件:Web回调事件。渲染流程中当HTTP响应的主体开始加载,新页面即将可见时触发该回调。此时文档加载还处于早期,因此链接的资源比如在线CSS、在线图片等可能尚不可用。
-
onRenderExited事件:应用渲染进程异常退出时触发该回调,可以在此回调中进行系统资源的释放、数据的保存等操作。如果应用希望异常恢复,需要调用loadUrl接口重新加载页面。详细用法参考应用如何避免Web组件渲染子进程异常退出导致的页面卡死问题。
-
onDisAppear事件:组件卸载消失时触发此回调。该事件在组件卸载时触发。
-
应用侧代码。
// xxx.ets import { webview } from '@kit.ArkWeb'; import { BusinessError } from '@kit.BasicServicesKit'; @Entry @Component struct WebComponent { controller: webview.WebviewController = new webview.WebviewController(); responseWeb: WebResourceResponse = new WebResourceResponse(); heads: Header[] = new Array(); @State webData: string = "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + "<title>intercept test</title>\n" + "</head>\n" + "<body>\n" + "<h1>intercept test</h1>\n" + "</body>\n" + "</html>"; aboutToAppear(): void { try { webview.WebviewController.setWebDebuggingAccess(true); } catch (error) { console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`); } } build() { Column() { Web({ src: 'www.example.com', controller: this.controller }) .onControllerAttached(() => { // 推荐在此loadUrl、设置自定义用户代理、注入JS对象等 console.info('onControllerAttached execute') }) .onLoadIntercept((event) => { if (event) { console.info('onLoadIntercept url:' + event.data.getRequestUrl()) console.info('url:' + event.data.getRequestUrl()) console.info('isMainFrame:' + event.data.isMainFrame()) console.info('isRedirect:' + event.data.isRedirect()) console.info('isRequestGesture:' + event.data.isRequestGesture()) } // 返回true表示阻止此次加载,否则允许此次加载 return false; }) .onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => { if (webResourceRequest && webResourceRequest.getRequestUrl() == "about:blank") { return true; } return false; }) .onInterceptRequest((event) => { if (event) { console.info('url:' + event.request.getRequestUrl()); } let head1: Header = { headerKey: "Connection", headerValue: "keep-alive" } let head2: Header = { headerKey: "Cache-Control", headerValue: "no-cache" } // 将新元素追加到数组的末尾,并返回数组的新长度。 let length = this.heads.push(head1); length = this.heads.push(head2); console.info('The response header result length is :' + length); this.responseWeb.setResponseHeader(this.heads); this.responseWeb.setResponseData(this.webData); this.responseWeb.setResponseEncoding('utf-8'); this.responseWeb.setResponseMimeType('text/html'); this.responseWeb.setResponseCode(200); this.responseWeb.setReasonMessage('OK'); // 返回响应数据则按照响应数据加载,无响应数据则返回null表示按照原来的方式加载 return this.responseWeb; }) .onPageBegin((event) => { if (event) { console.info('onPageBegin url:' + event.url); } }) .onFirstContentfulPaint(event => { if (event) { console.info("onFirstContentfulPaint:" + "[navigationStartTick]:" + event.navigationStartTick + ", [firstContentfulPaintMs]:" + event.firstContentfulPaintMs); } }) .onProgressChange((event) => { if (event) { console.info('newProgress:' + event.newProgress); } }) .onPageEnd((event) => { // 推荐在此事件中执行JavaScript脚本 if (event) { console.info('onPageEnd url:' + event.url); } }) .onPageVisible((event) => { console.info('onPageVisible url:' + event.url); }) .onRenderExited((event) => { if (event) { console.info('onRenderExited reason:' + event.renderExitReason); } }) .onDisAppear(() => { this.getUIContext().getPromptAction().showToast({ message: 'The web is hidden', duration: 2000 }) }) } } }
代码逻辑走读:
- 初始化Web组件:
- 定义了一个名为
WebComponent的组件,使用@Entry和@Component装饰器标记为入口组件。 - 初始化了
webview.WebviewController、WebResourceResponse和Header[]类型的变量。 - 定义了一个状态变量
webData,存储HTML内容。
- 定义了一个名为
- 生命周期方法
aboutToAppear:- 在组件即将显示时,尝试设置Web调试访问权限为
true。 - 使用
try-catch块捕获可能的BusinessError,并输出错误代码和信息。
- 在组件即将显示时,尝试设置Web调试访问权限为
- 构建UI:
- 使用
Column布局组件来组织Web组件。 - 创建一个
Web组件,设置其源地址为www.example.com,并绑定到webview.WebviewController。
- 使用
- 事件监听器:
onControllerAttached:在控制器附加时执行,可用于加载URL、设置用户代理、注入JavaScript对象等。onLoadIntercept:在加载拦截时执行,用于检查加载事件的详细信息,如URL、是否为主框架、是否为重定向等。onOverrideUrlLoading:在URL加载前执行,用于自定义处理逻辑,如重定向特定URL。onInterceptRequest:在请求拦截时执行,用于修改请求头或响应数据。onPageBegin:在页面开始加载时执行,可用于记录页面信息。onFirstContentfulPaint:在首次内容绘制时执行,用于记录页面加载性能。onProgressChange:在页面加载进度变化时执行,用于显示加载进度。onPageEnd:在页面加载完成时执行,可用于执行JavaScript脚本。onPageVisible:在页面可见时执行,可用于记录页面可见状态。onRenderExited:在渲染退出时执行,用于处理渲染退出事件。onDisAppear:在组件消失时执行,用于显示提示信息。
- 响应处理:
- 在
onInterceptRequest中,构建并设置自定义的响应头和数据,然后返回该响应以替代原始加载。
- 在
- 日志输出:
- 在各个事件监听器中,通过
console.info输出关键事件的详细信息,以便于调试和监控。
- 在各个事件监听器中,通过
Web组件网页加载的性能指标
网页加载过程中需要关注一些重要的性能指标。例如,FCP(First Contentful Paint)首次内容绘制,FMP(First Meaningful Paint)首次有效绘制,LCP(Largest Contentful Paint)最大内容绘制等。Web组件提供了如下接口来通知开发者,接口仅支持在线非PDF网页,不支持本地网页和PDF网页。
- onFirstContentfulPaint事件:网页首次内容绘制的回调函数。首次绘制文本、图像、非空白Canvas或SVG的时间点。
- onFirstMeaningfulPaint事件:网页绘制页面主要内容的回调函数。首次绘制主要内容的时间点。
- onLargestContentfulPaint事件:网页绘制页面最大内容的回调函数。绘制可视区域内最大图片、文本块或视频的时间点。
应用如何避免Web组件渲染子进程异常退出导致的页面卡死问题
ArkWeb(方舟Web)是一个Web组件平台,旨在为应用程序提供展示Web页面内容的功能,并向开发者提供一系列的能力,如页面加载、交互和调试等功能。使用ArkWeb相关应用时,可能因各种原因(例如前端偶现异常导致ArkWeb渲染子进程崩溃,或是打开的应用较多,系统资源紧张导致后台ArkWeb渲染子进程被终止)而出现页面卡死的问题,这时需要重新打开页面或重启应用来解决。
在ArkWeb渲染子进程异常退出导致页面卡死后,应用可通过监听onRenderExited事件来获取具体的退出原因RenderExitReason,并在异常回调中根据退出的具体原因,执行相应的异常处理。
开发实践案例
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
needReloadWhenVisible: boolean = false; // Web组件不可见时render退出后阻止重新加载页面,在可见时重新加载页面。
webIsVisible: boolean = false; // 判断Web组件是否可见。
// 此处是将子进程异常崩溃和其它异常原因做了区分,应用开发者可根据实际业务特点,细化对应异常的处理策略。
renderReloadMaxForCrashed: number = 5; // 设置因为异常崩溃后重新加载的最大重试次数,应用可根据业务特点,自行设置试错上限。
renderReloadCountForCrashed: number = 0; // 异常崩溃后重新加载的次数。
renderReloadMaxForOthers: number = 10; // 设置因为其它异常原因退出的最大重试次数,应用可根据业务特点,自行设置试错上限。
renderReloadCountForOthers: number = 0; // 其它异常原因退出后重新加载的次数。
// 创建Web组件。
controller: webview.WebviewController = new webview.WebviewController();
// 指定加载的页面。
url: string = "www.example.com";
build() {
Column() {
Web({ src: this.url, controller: this.controller })
.onVisibleAreaChange([0, 1.0], (isVisible) => {
this.webIsVisible = isVisible;
if (isVisible && this.needReloadWhenVisible) { // Web组件可见时重新加载页面。
this.needReloadWhenVisible = false;
this.controller.loadUrl(this.url);
}
})
// 应用监听渲染子进程异常退出回调,并进行异常处理。
.onRenderExited((event) => {
if (!event) {
return;
}
if (event.renderExitReason == RenderExitReason.ProcessCrashed) {
if (this.renderReloadCountForCrashed >= this.renderReloadMaxForCrashed) {
// 设置重试次数上限保护,避免必现问题导致页面被循环加载。
return;
}
console.info('renderReloadCountForCrashed: ' + this.renderReloadCountForCrashed);
this.renderReloadCountForCrashed++;
} else {
if (this.renderReloadCountForOthers >= this.renderReloadMaxForOthers) {
// 设置重试次数上限保护, 避免必现问题导致页面被循环加载。
return;
}
console.info('renderReloadCountForOthers: ' + this.renderReloadCountForOthers);
this.renderReloadCountForOthers++;
}
if (this.webIsVisible) {
// Web组件可见则立即重新加载。
this.controller.loadUrl(this.url);
return;
}
// Web组件不可见时不立即重新加载。
this.needReloadWhenVisible = true;
})
}
}
}
代码逻辑走读:
- 组件初始化:
- 定义了两个布尔类型的状态变量
needReloadWhenVisible和webIsVisible,用于控制页面在Web视图可见时是否重新加载。 - 设置了两个变量
renderReloadMaxForCrashed和renderReloadMaxForOthers,分别用于定义因崩溃和其它原因导致的最大重试次数。 - 初始化了
renderReloadCountForCrashed和renderReloadCountForOthers,用于记录重试次数。 - 创建了一个
WebviewController实例,用于控制Web视图。
- 定义了两个布尔类型的状态变量
- Web视图创建:
- 使用
Web组件创建一个Web视图,并指定要加载的页面URL。 - 通过
onVisibleAreaChange方法监听Web视图的可见状态,当Web视图可见且needReloadWhenVisible为真时,调用loadUrl方法重新加载页面。
- 使用
- 异常处理:
- 使用
onRenderExited方法监听Web视图的异常退出事件。 - 根据异常退出原因(崩溃或其它)和当前重试次数,决定是否进行页面重新加载。
- 如果异常退出原因为崩溃,且重试次数未达到最大值,则增加崩溃重试次数并尝试重新加载页面。
- 如果异常退出原因为其它,且重试次数未达到最大值,则增加其它原因重试次数并尝试重新加载页面。
- 使用
更多推荐



所有评论(0)