欢迎光临
我们一直在努力

HarmonyOS ArkWeb 系列之组件生命周期全解:从加载到渲染的每个关键节点

文章目录

      • 先把生命周期的顺序理清楚
      • onControllerAttached:最早能操作的时机
      • onPageBegin 和 onPageEnd:loading 动画的起止点
      • onFirstContentfulPaint:性能监控的关键指标
      • onLoadIntercept:URL 级别的拦截
      • onInterceptRequest:响应级别的替换
      • onRenderExited:渲染进程崩溃了怎么办
      • 完整的生命周期示例
      • 各回调触发时机速查表
      • 写在最后

你有没有遇到这种场景:想在网页加载完之后执行一段 JS,却发现注入太早 DOM 还没就绪;或者想在页面开始加载时显示 loading 动画,结果时机不对?这些问题的根源都是没搞清楚 Web 组件的加载生命周期。

先把生命周期的顺序理清楚

Web 组件从创建到内容可见,大致经过这几个阶段:

onControllerAttached:最早能操作的时机

这是 Web 组件控制器和内核绑定完成后的第一个回调。很多初始化操作应该在这里做:

Web({ src: 'https://www.baidu.com', controller: this.controller })
.onControllerAttached(() => {
// ✅ 推荐在此做初始化
// 1. 动态设置用户代理
this.controller.setCustomUserAgent('MyApp/1.0 HarmonyOS');
// 2. 注入 JS 对象(让网页可以调用原生方法)
this.controller.registerJavaScriptProxy({
/* … */
}, 'NativeBridge', ['methodName']);
// 3. 动态 loadUrl
// this.controller.loadUrl('https://another.com');
console.info('控制器已绑定,可以开始初始化了');
})

注意:在 onControllerAttached 之前调用控制器方法(比如在组件创建时就调 loadUrl)会抛异常。

onPageBegin 和 onPageEnd:loading 动画的起止点

这两个是最常用的。

import { webview } from '@kit.ArkWeb';

@Entry
@Component
struct WebLifecycleDemo {
controller: webview.WebviewController = new webview.WebviewController();
@State isLoading: boolean = false;
@State loadProgress: number = 0;

build() {
Column() {
// loading 指示器:仅在加载时显示
if (this.isLoading) {
Row() {
LoadingProgress()
.width(24).height(24)
Text(`加载中 ${this.loadProgress}%`)
.fontSize(14)
.margin({ left: 8 })
}
.padding(8)
.width('100%')
}

// 进度条:加载时显示
Progress({
value: this.loadProgress,
total: 100,
type: ProgressType.Linear
})
.width('100%')
.opacity(this.isLoading ? 1 : 0)

Web({ src: 'https://www.baidu.com', controller: this.controller })
.onPageBegin((event) => {
// 页面开始加载:显示 loading
this.isLoading = true;
this.loadProgress = 0;
if (event) {
console.info('开始加载:', event.url);
}
})
.onProgressChange((event) => {
// 进度更新:0~100
if (event) {
this.loadProgress = event.newProgress;
}
})
.onPageEnd((event) => {
// 页面加载完成:隐藏 loading,执行 JS
this.isLoading = false;
if (event) {
console.info('加载完成:', event.url);
// ✅ 推荐在此执行 JS,DOM 已就绪
this.controller.runJavaScript('document.title')
.then((title) => {
console.info('页面标题:', title);
});
}
})
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
}
}

onFirstContentfulPaint:性能监控的关键指标

FCP(First Contentful Paint)是衡量网页性能的核心指标之一,表示用户看到第一帧有意义内容的时间。

Web({ src: 'https://www.baidu.com', controller: this.controller })
.onFirstContentfulPaint(event => {
if (event) {
console.info('FCP 数据:',
'导航开始时间(tick):', event.navigationStartTick,
'首帧绘制耗时(ms):', event.firstContentfulPaintMs
);
// 可以上报到性能监控系统
// reportPerformance('FCP', event.firstContentfulPaintMs);
}
})

navigationStartTick 是单调递增的时间戳,firstContentfulPaintMs 是从导航开始到首帧绘制的毫秒数。

onLoadIntercept:URL 级别的拦截

在页面真正加载之前,可以检查 URL 决定是否允许加载:

Web({ src: 'https://www.baidu.com', controller: this.controller })
.onLoadIntercept((event) => {
if (event) {
const url = event.data.getRequestUrl();
const isMainFrame = event.data.isMainFrame();
const isRedirect = event.data.isRedirect();

console.info('拦截检查:', url, '主框架:', isMainFrame);

// 过滤掉某些域名
if (url.includes('ads.example.com')) {
return true; // 返回 true = 阻止加载
}
}
return false; // 返回 false = 允许加载
})

onInterceptRequest:响应级别的替换

这个比 onLoadIntercept 更强大——不仅能阻止,还能替换响应内容:

Web({ src: 'https://www.example.com', controller: this.controller })
.onInterceptRequest((event) => {
if (event) {
const url = event.request.getRequestUrl();
console.info('请求拦截:', url);

// 把某个 URL 的响应替换为本地内容
if (url === 'https://www.example.com/offline.html') {
const response = new WebResourceResponse();
response.setResponseData('<h1>离线模式</h1>');
response.setResponseEncoding('utf-8');
response.setResponseMimeType('text/html');
response.setResponseCode(200);
response.setReasonMessage('OK');
return response;
}
}
return null; // 返回 null = 不拦截,按原来方式加载
})

onRenderExited:渲染进程崩溃了怎么办

Web 渲染是在独立进程里跑的,极端情况下这个进程会退出(OOM、崩溃等)。这时候 onRenderExited 会触发:

Web({ src: 'https://www.example.com', controller: this.controller })
.onRenderExited((event) => {
if (event) {
console.error('渲染进程退出,原因:', event.renderExitReason);
// 可以提示用户并提供重新加载按钮
this.showReloadTip = true;
}
})

renderExitReason 的可能值包括:

  • RenderExitReason.ProcessAbnormalTermination:进程异常终止
  • RenderExitReason.ProcessWasKilled:进程被系统杀死
  • RenderExitReason.ProcessCrashed:进程崩溃

完整的生命周期示例

把上面所有回调整合到一起:

import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct WebFullLifecycleDemo {
controller: webview.WebviewController = new webview.WebviewController();
responseWeb: WebResourceResponse = new WebResourceResponse();

aboutToAppear(): void {
// 开启 Web 调试(开发阶段使用,生产环境关闭)
try {
webview.WebviewController.setWebDebuggingAccess(true);
} catch (error) {
console.error(`开启调试失败: ${(error as BusinessError).message}`);
}
}

build() {
Column() {
Web({ src: 'https://www.example.com', controller: this.controller })
.onControllerAttached(() => {
console.info('① 控制器绑定完成,开始初始化');
})
.onLoadIntercept((event) => {
if (event) {
console.info('② URL 拦截检查:', event.data.getRequestUrl());
}
return false; // 允许加载
})
.onInterceptRequest((event) => {
if (event) {
console.info('③ 请求拦截:', event.request.getRequestUrl());
}
return null; // 不替换响应
})
.onPageBegin((event) => {
if (event) {
console.info('④ 页面开始加载:', event.url);
}
})
.onProgressChange((event) => {
if (event) {
console.info('⑤ 加载进度:', event.newProgress + '%');
}
})
.onFirstContentfulPaint(event => {
if (event) {
console.info('⑥ 首帧绘制耗时:', event.firstContentfulPaintMs, 'ms');
}
})
.onPageEnd((event) => {
if (event) {
console.info('⑦ 页面加载完成:', event.url);
// 现在可以安全地执行 JS
}
})
.onPageVisible((event) => {
console.info('⑧ 页面内容可见:', event.url);
})
.onRenderExited((event) => {
if (event) {
console.error('⑨ 渲染进程退出:', event.renderExitReason);
}
})
.onDisAppear(() => {
this.getUIContext().getPromptAction().showToast({
message: 'Web 组件已隐藏',
duration: 2000
});
})
}
}
}

各回调触发时机速查表

回调触发时机常见用途
onControllerAttached 控制器绑定完成 初始化、设置用户代理、注入对象
onLoadIntercept URL 即将加载前 拦截特定 URL
onInterceptRequest 资源请求发出前 替换响应内容(离线缓存)
onPageBegin 页面开始加载 显示 loading
onProgressChange 加载进度变化 更新进度条
onFirstContentfulPaint 首帧内容绘制 性能监控
onPageEnd 页面加载完成 隐藏 loading,执行 JS
onPageVisible 页面内容可见 统计页面展示
onRenderExited 渲染进程退出 显示重新加载提示
onDisAppear Web 组件从视图树移除 清理资源

写在最后

生命周期这块掌握了,很多"时机问题"就迎刃而解了。记住核心规则:初始化放 onControllerAttached,JS 脚本放 onPageEnd,loading 动画放 onPageBegin 开、onPageEnd 关。

赞(0)
未经允许不得转载:171主机测评 » HarmonyOS ArkWeb 系列之组件生命周期全解:从加载到渲染的每个关键节点
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址