1# Web组件的生命周期 2 3## 概述 4 5开发者可以使用Web组件加载本地或者在线网页。 6 7Web组件提供了丰富的组件生命周期回调接口,通过这些回调接口,开发者可以感知Web组件的生命周期状态变化,进行相关的业务处理。 8 9Web组件的状态主要包括:Controller绑定到Web组件、网页加载开始、网页加载进度、网页加载结束、页面即将可见等。 10 11**图1** Web组件网页正常加载过程中的回调事件 12 13 14 15## Web组件网页加载的状态说明 16 17- [aboutToAppear](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#abouttoappear)函数:在创建自定义组件的新实例后,在执行其build函数前执行。一般建议在此设置WebDebug调试模式[setWebDebuggingAccess](../reference/apis-arkweb/js-apis-webview.md#setwebdebuggingaccess)、设置Web内核自定义协议URL的跨域请求与fetch请求的权限[customizeSchemes](../reference/apis-arkweb/js-apis-webview.md#customizeschemes)、设置Cookie([configCookie](../reference/apis-arkweb/js-apis-webview.md#configcookie11-1))等。 18 19- [onControllerAttached](../reference/apis-arkweb/ts-basic-components-web.md#oncontrollerattached10)事件:当Controller成功绑定到Web组件时触发该回调,且禁止在该事件回调前调用Web组件相关的接口,否则会抛出js-error异常。推荐在此事件中注入JS对象[registerJavaScriptProxy](../reference/apis-arkweb/js-apis-webview.md#registerjavascriptproxy)、设置自定义用户代理[setCustomUserAgent](../reference/apis-arkweb/js-apis-webview.md#setcustomuseragent10),可以在回调中使用[loadUrl](../reference/apis-arkweb/js-apis-webview.md#loadurl),[getWebId](../reference/apis-arkweb/js-apis-webview.md#getwebid)等操作网页不相关的接口。但因该回调调用时网页还未加载,因此无法在回调中使用有关操作网页的接口,例如[zoomIn](../reference/apis-arkweb/js-apis-webview.md#zoomin)、[zoomOut](../reference/apis-arkweb/js-apis-webview.md#zoomout)等。 20 21- [onLoadIntercept](../reference/apis-arkweb/ts-basic-components-web.md#onloadintercept10)事件:当Web组件加载url之前触发该回调,用于判断是否阻止此次访问。默认允许加载。 22 23- [onOverrideUrlLoading](../reference/apis-arkweb/ts-basic-components-web.md#onoverrideurlloading12)事件:当URL将要加载到当前Web中时,让宿主应用程序有机会获得控制权,回调函数返回true将导致当前Web中止加载URL,而返回false则会导致Web继续照常加载URL。onLoadIntercept接口和onOverrideUrlLoading接口行为不一致,触发时机也不同,所以在应用场景上存在一定区别。主要是在LoadUrl和iframe(HTML标签,表示HTML内联框架元素,用于将另一个页面嵌入到当前页面中)加载时,onLoadIntercept事件会正常回调到,但onOverrideUrlLoading事件在LoadUrl加载时不会触发,在iframe加载HTTP(s)协议或about:blank时也不会触发。详细介绍请见[onLoadIntercept](../reference/apis-arkweb/ts-basic-components-web.md#onloadintercept10)和[onOverrideUrlLoading](../reference/apis-arkweb/ts-basic-components-web.md#onoverrideurlloading12)的说明。 24 25- [onInterceptRequest](../reference/apis-arkweb/ts-basic-components-web.md#oninterceptrequest9)事件:当Web组件加载url之前触发该回调,用于拦截url并返回响应数据。 26 27- [onPageBegin](../reference/apis-arkweb/ts-basic-components-web.md#onpagebegin)事件:网页开始加载时触发该回调,且只在主frame(表示一个HTML元素,用于展示HTML页面的HTML元素)触发。如果是iframe或者frameset(用于包含frame的HTML标签)的内容加载时则不会触发此回调。多frame页面有可能同时开始加载,即使主frame已经加载结束,子frame也有可能才开始或者继续加载中。同一页面导航(片段、历史状态等)或者在提交前失败、被取消的导航等也不会触发该回调。 28 29- [onProgressChange](../reference/apis-arkweb/ts-basic-components-web.md#onprogresschange)事件:告知开发者当前页面加载的进度。多frame页面或者子frame有可能还在继续加载而主frame可能已经加载结束,所以在[onPageEnd](../reference/apis-arkweb/ts-basic-components-web.md#onpageend)事件后依然有可能收到该事件。 30 31- [onPageEnd](../reference/apis-arkweb/ts-basic-components-web.md#onpageend)事件:网页加载完成时触发该回调,且只在主frame触发。多frame页面有可能同时开始加载,即使主frame已经加载结束,子frame也有可能才开始或者继续加载中。同一页面导航(片段、历史状态等)或者在提交前失败、被取消的导航等也不会触发该回调。推荐在此回调中执行JavaScript脚本[loadUrl](../reference/apis-arkweb/js-apis-webview.md#loadurl)等。需要注意的是收到该回调并不能保证Web绘制的下一帧将反映此时DOM的状态。 32 33- [onPageVisible](../reference/apis-arkweb/ts-basic-components-web.md#onpagevisible9)事件:Web回调事件。渲染流程中当HTTP响应的主体开始加载,新页面即将可见时触发该回调。此时文档加载还处于早期,因此链接的资源比如在线CSS、在线图片等可能尚不可用。 34 35- [onRenderExited](../reference/apis-arkweb/ts-basic-components-web.md#onrenderexited9)事件:应用渲染进程异常退出时触发该回调,可以在此回调中进行系统资源的释放、数据的保存等操作。如果应用希望异常恢复,需要调用[loadUrl](../reference/apis-arkweb/js-apis-webview.md#loadurl)接口重新加载页面。 36 37- [onDisAppear](../reference/apis-arkui/arkui-ts/ts-universal-events-show-hide.md#ondisappear)事件:组件卸载消失时触发此回调。该事件为通用事件,指组件从组件树上卸载时触发的事件。 38 39应用侧代码。 40 41 ```ts 42 // xxx.ets 43 import { webview } from '@kit.ArkWeb'; 44 import { BusinessError } from '@kit.BasicServicesKit'; 45 import { promptAction } from '@kit.ArkUI'; 46 47 @Entry 48 @Component 49 struct WebComponent { 50 controller: webview.WebviewController = new webview.WebviewController(); 51 responseWeb: WebResourceResponse = new WebResourceResponse(); 52 heads: Header[] = new Array(); 53 @State webData: string = "<!DOCTYPE html>\n" + 54 "<html>\n" + 55 "<head>\n" + 56 "<title>intercept test</title>\n" + 57 "</head>\n" + 58 "<body>\n" + 59 "<h1>intercept test</h1>\n" + 60 "</body>\n" + 61 "</html>"; 62 63 aboutToAppear(): void { 64 try { 65 webview.WebviewController.setWebDebuggingAccess(true); 66 } catch (error) { 67 console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`); 68 } 69 } 70 71 build() { 72 Column() { 73 Web({ src: $rawfile('index.html'), controller: this.controller }) 74 .onControllerAttached(() => { 75 // 推荐在此loadUrl、设置自定义用户代理、注入JS对象等 76 console.log('onControllerAttached execute') 77 }) 78 .onLoadIntercept((event) => { 79 if (event) { 80 console.log('onLoadIntercept url:' + event.data.getRequestUrl()) 81 console.log('url:' + event.data.getRequestUrl()) 82 console.log('isMainFrame:' + event.data.isMainFrame()) 83 console.log('isRedirect:' + event.data.isRedirect()) 84 console.log('isRequestGesture:' + event.data.isRequestGesture()) 85 } 86 // 返回true表示阻止此次加载,否则允许此次加载 87 return true 88 }) 89 .onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => { 90 if (webResourceRequest && webResourceRequest.getRequestUrl() == "about:blank") { 91 return true; 92 } 93 return false; 94 }) 95 .onInterceptRequest((event) => { 96 if (event) { 97 console.log('url:' + event.request.getRequestUrl()); 98 } 99 let head1: Header = { 100 headerKey: "Connection", 101 headerValue: "keep-alive" 102 } 103 let head2: Header = { 104 headerKey: "Cache-Control", 105 headerValue: "no-cache" 106 } 107 let length = this.heads.push(head1); 108 length = this.heads.push(head2); 109 this.responseWeb.setResponseHeader(this.heads); 110 this.responseWeb.setResponseData(this.webData); 111 this.responseWeb.setResponseEncoding('utf-8'); 112 this.responseWeb.setResponseMimeType('text/html'); 113 this.responseWeb.setResponseCode(200); 114 this.responseWeb.setReasonMessage('OK'); 115 // 返回响应数据则按照响应数据加载,无响应数据则返回null表示按照原来的方式加载 116 return this.responseWeb; 117 }) 118 .onPageBegin((event) => { 119 if (event) { 120 console.log('onPageBegin url:' + event.url); 121 } 122 }) 123 .onFirstContentfulPaint(event => { 124 if (event) { 125 console.log("onFirstContentfulPaint:" + "[navigationStartTick]:" + 126 event.navigationStartTick + ", [firstContentfulPaintMs]:" + 127 event.firstContentfulPaintMs); 128 } 129 }) 130 .onProgressChange((event) => { 131 if (event) { 132 console.log('newProgress:' + event.newProgress); 133 } 134 }) 135 .onPageEnd((event) => { 136 // 推荐在此事件中执行JavaScript脚本 137 if (event) { 138 console.log('onPageEnd url:' + event.url); 139 } 140 }) 141 .onPageVisible((event) => { 142 console.log('onPageVisible url:' + event.url); 143 }) 144 .onRenderExited((event) => { 145 if (event) { 146 console.log('onRenderExited reason:' + event.renderExitReason); 147 } 148 }) 149 .onDisAppear(() => { 150 promptAction.showToast({ 151 message: 'The web is hidden', 152 duration: 2000 153 }) 154 }) 155 } 156 } 157 } 158 ``` 159 160前端index.html。 161 162 ```html 163 <!-- index.html --> 164 <!DOCTYPE html> 165 <html> 166 <head> 167 <meta charset="UTF-8"> 168 </head> 169 <body> 170 <h1>Hello, ArkWeb</h1> 171 </body> 172 </html> 173 ``` 174 175## Web组件网页加载的性能指标 176 177网页加载过程中需要关注一些重要的性能指标。例如,FCP(First Contentful Paint)首次内容绘制,FMP(First Meaningful Paint)首次有效绘制,LCP(Largest Contentful Paint)最大内容绘制等。Web组件提供了如下接口来通知开发者。 178 179- [onFirstContentfulPaint](../reference/apis-arkweb/ts-basic-components-web.md#onfirstcontentfulpaint10)事件:网页首次内容绘制的回调函数。首次绘制文本、图像、非空白Canvas或者SVG的时间点。 180 181- [onFirstMeaningfulPaint](../reference/apis-arkweb/ts-basic-components-web.md#onfirstmeaningfulpaint12)事件:网页首次有效绘制的回调函数。首次绘制页面主要内容的时间点。 182 183- [onLargestContentfulPaint](../reference/apis-arkweb/ts-basic-components-web.md#onlargestcontentfulpaint12)事件:网页绘制页面最大内容的回调函数。可视区域内容最大的可见元素开始出现在页面上的时间点。