首页 > 编程笔记 > 通用技能 阅读:5

鸿蒙Web组件的生命周期是什么(新手必看)

我们可以使用 Web 组件加载本地或者在线网页。Web 组件提供了丰富的组件生命周期回调接口,通过这些回调接口,开发者可以感知 Web 组件的生命周期状态变化,并进行相关的业务处理。

Web 组件的状态主要包括:将 Controller(控制器)绑定到 Web 组件、网页加载开始、网页加载进度、网页加载结束、页面即将可见等。Web 组件的生命周期示意图如下图所示:


图 1 Web组件的生命周期示意图

1) onControllerAttached事件

当 Controller 成功绑定到 Web 组件时,会触发该回调,并且禁止在该事件回调之前调用与 Web 组件相关的接口,否则会抛出 js-error 异常。建议在此回调中注入 JavaScript 对象 registerJavaScriptProxy,并设置自定义用户代理 setCustomUserAgent。

在回调中,可以使用如 loadUrl、getWebId 等与网页本身无关的接口。然而,由于此时网页尚未加载完成,因此无法在回调中使用涉及网页操作的接口,如 zoomIn、zoomOut 等。

2) onLoadIntercept事件

在 Web 组件加载 URL 之前触发该回调,用于判断是否阻止此次访问。默认允许加载。

3) onInterceptRequest事件

在 Web 组件加载 URL 之前触发该回调,用于拦截 URL 并返回响应数据。

4) onPageBegin事件

在网页开始加载时触发该回调,并且只在主 frame(框架,表示一个 HTML 元素,用于展示 HTML 页面的 HTML 元素)中触发。如果加载的是 iframe 或者 frameset(用于包含 frame 的 HTML 标签)的内容,则不会触发此回调。

在多 frame 页面中,多个 frame 有可能同时开始加载,因此有可能出现主 frame 已经加载完成而子 frame 才开始加载或者仍在加载的情况。此外,同一页面的导航(如片段导航、历史状态切换等),或者在提交前失败、被取消的导航等,也不会触发该回调。

5) onProgressChange事件

用于获取当前页面加载的进度。在多 frame 页面中,子 frame 有可能还在继续加载,而主 frame 已经加载完成。因此,在触发 onPageEnd 事件后,依然有可能收到 onProgressChange 事件。

6) onPageEnd事件

网页加载完成时触发该回调,且只在主 frame 上触发。多 frame 页面有可能同时开始加载,即使主 frame 已经加载结束,子 frame 也有可能才开始或者继续加载中。同一页面的导航(如片段导航、历史状态切换等),或者在提交前失败、被取消的导航等,也不会触发该回调。推荐在此回调中执行 JavaScript 脚本,如 loadUrl 等。

需要注意的是,收到该回调并不能保证 Web 绘制的下一帧将反映此时 DOM 的状态。

Web组件加载的其他事件

1) aboutToAppear事件

aboutToAppear 事件在创建自定义组件的新实例后,在执行其 build 函数前执行。一般建议在此设置 WebDebug 调试模式 setWebDebuggingAccess,设置 Web 内核自定义协议 URL 的跨域请求与 fetch 请求的权限 customizeSchemes,设置 Cookie(configCookie) 等。

2) onOverrideUrlLoading事件

当 URL 将要加载到当前 Web 中时,onOverrideUrlLoading 事件让宿主应用程序有机会获得控制权。回调函数返回 true 会使当前 Web 中止加载 URL,返回 false 会使 Web 继续照常加载 URL。onLoadIntercept 接口和 onOverrideUrlLoading 接口行为不一致,触发时机也不同,所以它们在应用场景上存在一定区别。

具体来说,在加载 LoadUrl 和 iframe 时,onLoadIntercept 事件会正常回调,但 onOverrideUrlLoading 事件在加载 LoadUrl 时不会触发,在 iframe 加载 HTTP(s) 协议或 about:blank 时也不会触发。

3) onPageVisible事件

onPageVisible 事件是 Web 回调事件。在渲染流程中,当 HTML 响应的主体开始加载,新页面即将可见时触发该回调。此时文档加载还处于早期,因此链接的资源(比如在线 CSS、在线图片等)可能尚不可用。

4) onRenderExited事件

应用渲染进程异常退出时触发该回调,在此回调中可以进行系统资源的释放、数据的保存等操作。如果应用希望进行异常恢复,则需要调用 loadUrl 接口重新加载页面。

5) onDisAppear事件

onDisAppear 事件为通用事件,当组件从组件树上卸载时触发该事件。

下面通过一个示例,来更好地认识 ArkWeb 组件的回调函数,示例代码如下:
<!-- demo.ets -->
// 导入 ArkWeb 组件
import { webview } from '@kit.ArkWeb';
// 导入业务异常
import { BusinessError } from '@kit.BasicServicesKit';
// 导入弹出窗
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Demo0201 {
  /* ========= Web 相关对象 ========= */
  // 创建 Web 控制器对象
  controller: webview.WebviewController = new webview.WebviewController();
  // 创建 Web 响应对象
  responseWeb: WebResourceResponse = new WebResourceResponse();
  // 创建请求消息头数组
  heads: Header[] = new Array();

  // 准备一段本地 HTML 内容,用于拦截请求时返回
  @State webData: string =
    "<!DOCTYPE html>\n" +
    "<html>\n" +
    "<head>\n" +
    "<title>HarmonyOS 开发之路</title>\n" +
    "</head>\n" +
    "<body>\n" +
    "<h1>欢迎进入 HarmonyOS 开发殿堂</h1>\n" +
    "</body>\n" +
    "</html>";

  /* ========= 生命周期 ========= */
  aboutToAppear(): void {
    // 开启 Web 调试
    try {
      webview.WebviewController.setWebDebuggingAccess(true);
      console.log('aboutToAppear 执行了');
    } catch (error) {
      console.error(
        `错误码:${(error as BusinessError).code}, 错误信息:${(error as BusinessError).message}`
      );
    }
  }

  /* ========= UI 构建 ========= */
  build() {
    Column() {
      Web({
        src: $rawfile('demo0201.html'),   // 加载本地 html 文件
        controller: this.controller
      })
        // 控制器挂载完成(推荐在此 loadUrl、注入 JS 对象等)
        .onControllerAttached(() => {
          console.log('onControllerAttached 方法执行');
        })
        // 拦截即将加载的 URL,返回 true 阻止加载
        .onLoadIntercept((event) => {
          if (event) {
            console.log('onLoadIntercept 执行了:' + event.data.getRequestUrl());
          }
          // 返回 true 表示阻止此次加载,否则允许
          return true;
        })
        // 覆盖 URL 加载策略
        .onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => {
          if (
            webResourceRequest &&
            webResourceRequest.getRequestUrl() === 'about:blank'
          ) {
            return true;
          }
          return false;
        })
        // 拦截资源请求(可返回自定义响应)
        .onInterceptRequest((event) => {
          if (event) {
            console.log('onInterceptRequest 执行了:' + event.request.getRequestUrl());

            // 构造响应头
            this.heads.push({
              headerKey: 'Connection',
              headerValue: 'keep-alive'
            });

            // 填充自定义响应
            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;
          }
          return null;
        })
        // 页面开始加载
        .onPageBegin((event) => {
          if (event) {
            console.log('onPageBegin 执行:' + event.url);
          }
        })
        // 首次内容绘制完成
        .onFirstContentfulPaint((event) => {
          if (event) {
            console.log('onFirstContentfulPaint 执行:' + event.firstContentfulPaintMs);
          }
        })
        // 加载进度变化
        .onProgressChange((event) => {
          if (event) {
            console.log('onProgressChange 执行:' + event.newProgress);
          }
        })
        // 页面加载结束(推荐在此执行 JS 脚本)
        .onPageEnd((event) => {
          if (event) {
            console.log('onPageEnd 执行:' + event.url);
          }
        })
        // 页面可见
        .onPageVisible((event) => {
          console.log('onPageVisible 执行:' + event.url);
        })
        // 渲染进程退出
        .onRenderExited((event) => {
          if (event) {
            console.log('onRenderExited 执行');
          }
        })
        // Web 组件消失
        .onDisAppear(() => {
          promptAction.showToast({
            message: 'Web 组件隐藏啦',
            duration: 2000
          });
        });
    }
  }
}

<!-- demo.html -->
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>Hello, ArkWeb</h1>
    </body>
</html>
运行效果如下图所示:


图 2 ArkWeb 组件事件回调演示

Web组件性能指标

在网页加载过程中,需要关注一些重要的性能指标,例如 FCP(First Contentful Paint,首次内容绘制)、FMP(First Meaningful Paint,首次有效绘制)、LCP(Largest Contentful Paint,最大内容绘制)等。

Web 组件提供了如下接口来进行通知:

相关文章