当前位置: 首页 > news >正文

【HarmonyOS】ArkWeb——从入门到入土

1.使用场景

  • 在应用内集成Web页面:应用可以在页面中使用Web组件,嵌入Web页面,从而降低开发成本
  • 浏览器网页浏览场景:浏览器类应用可以使用Web组件来打开第三方网页
  • 小程序:应用内嵌小程序功能的应用可以使用web组件来渲染小程序页面

2.ArkWeb进程

ArkWeb是多进程模型,分为应用进程、web渲染进程、webGpu渲染进程、web孵化进程和Foundation进程
在这里插入图片描述

  • 应用进程中的web进程(应用唯一)
    • 应用进程为主进程,包含UI主线程和Web的相关线程。包含网络线程、Video线程、Audio线程和IO线程
  • Foundation进程(系统唯一)
    • 负责接收应用进程要创建并启动一个新进程的请求,管理应用进程和web渲染进程的绑定关系
      这里的创建并启动并非从0开始创建,而是由一定的基础
  • Web孵化进程(系统唯一)
    • 负责接收Foundation进程的请求,执行孵化Web渲染进程与WebGpu进程
    • 孵化完毕后对新的进程进行权限降级处理,并且预加载一些动态库,以提升运行速度
  • Web渲染进程(应用可指定多Web实例之间共享或独立进程)
    • 负责运行Web渲染进程引擎
    • 负责运行ArkWeb执行引擎
    • 提供接口供应用选择多web实例之间是否共享渲染进程
  • WebGpu进程(应用唯一)
    • 指挥GPU进行光栅化等底层硬件相关操作

3.Web组件的生命周期

在这里插入图片描述

  • aboutToAppear:创建新自定义组件实例之后,在执行build前执行
  • onControllerAttached:当Controller成功绑定时触发该回调
  • onLoadIntercept:web组件加载url之前触发该回调,判断是否阻止此次访问
  • onInterceptRequest:web组件加载url之间触发该回调,用于拦截url并返回一个自定义的响应数据
  • onPageBegin:网页开始加载时触发该回调
  • onProgressChange:告知开发者当前页面加载进度
  • onPageEnd:网页加载完成触发该回调

4.Web组件的渲染和布局

web组件可以使用layoutMode(WebLayoutMode.FIT_CONTENT)属性实现组件高度跟随页面内容自适应变化

4.1异步渲染模式(默认)

在异步渲染模式下,web组件作为图形surface节点,独立送显。
建议在仅由web组件构成的页面中使用此模式,以提高性能

限制

  • web组件的宽高不能超过7680px,超过会导致白屏
  • 不支持动态切换模式
    在这里插入图片描述

当前页面由web组件作为主体显示应用页面,web组件仅需占满手机屏幕大小即可,超出的H5页面部分ArkWeb会自动生成滚动条,便于滑动浏览

4.2同步渲染模式

同步渲染模式下,web组件作为图形canvas节点,web渲染跟随系统组件一起送显,可以渲染更长的web组件内容

建议在web组件与其他ArkUI组件共同滑动交互时使用
在这里插入图片描述
当前页面有web组件和ArkUI组件共同组成,此时H5界面与Web组件的高度需要一致,web内部不生成滚动条,
作为一个超长组件展示,通过Scroll组件实现应用内部的滚动,确保用户平滑浏览

5.应用中使用前端页面的JS

5.1应用侧调用前端页面函数

应用侧可以通过runJavaScriptrunJavaScriptExt方法来调用前端页面的JS相关函数

参数类型有以下差异

  • runJavaScript仅支持string类型
  • runJavaScriptExt支持ArrayBuffer类型和string类型
//应用侧
.onClick(() => {// 调用前端页面无参函数。this.webviewController.runJavaScript('htmlTest()');
})/前端页面侧
<script>
//...// 无参函数。function htmlTest() {document.getElementById('text').style.color = 'yellow';}
//...
</script>

5.2前端页面调用应用侧函数

开发者使用web组件将应用侧代码注册到前端中,注册完成之后,前端页面

注册应用侧代码

  • 在web组件初始化调用,使用javaScriptProxy接口
  • 在web组价初始化完成后调用,使用registerJavaScriptProxy接口,这两种方式都需要和deleteJavaScriptRegister接口配合使用,防止内存泄漏
// Web组件加载本地index.html页面
Web({ src: $rawfile('index.html'), controller: this.webviewController})// 将对象注入到web端.javaScriptProxy({//定义要注入的JavaScript对象object: this.testObj,name: "testObjName",methodList: ["test"],controller: this.webviewController,// 可选参数asyncMethodList: [],//参与注册的应用侧js对象的异步方法permission: //json字符串,通过该字符串配置JSBridge的权限管控})
//前端页面使用
<script>function callArkTS() {let str = testObjName.test();document.getElementById("demo").innerHTML = str;console.info('ArkTS Hello World! :' + str);}
</script>

5.3建立应用侧与前端页面数据通道

前端页面和应用侧之间可以使用createWebMessagePorts接口创建消息端口来实现两端的通信

  • 应用侧通过createWebMessagePorts接口创建两个消息端口,再把其中一个端口通过postMessage接口发送到前端页面,便可以子啊前端页面和应用侧之间互相发送消息
//应用侧代码.onClick(() => {try {// 1、创建两个消息端口。this.ports = this.controller.createWebMessagePorts();if (this.ports && this.ports[0] && this.ports[1]) {// 2、在应用侧的消息端口(如端口1)上注册回调事件。this.ports[1].onMessageEvent((result: webview.WebMessage) => {let msg = 'Got msg from HTML:';if (typeof (result) === 'string') {console.info(`received string message from html5, string is: ${result}`);msg = msg + result;} else if (typeof (result) === 'object') {if (result instanceof ArrayBuffer) {console.info(`received arraybuffer from html5, length is: ${result.byteLength}`);msg = msg + 'length is ' + result.byteLength;} else {console.info('not support');}} else {console.info('not support');}this.receivedFromHtml = msg;})// 3、将另一个消息端口(如端口0)发送到HTML侧,由HTML侧保存并使用。this.controller.postMessage('__init_port__', [this.ports[0]], '*');} else {console.error(`ports is null, Please initialize first`);}} catch (error) {console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);}})

6.管理Web组件的网页加载

6.1加载页面

6.1.1加载网络页面

  • 默认加载:在web组件的src字段中设置(不能通过状态变量改变地址)
  • 修改地址:使用webController的loadUrl方法重新加载

6.1.2加载本地页面

  • 默认加载:在web组件的src字段中使用$rawfile()绑定
  • 修改地址同样使用loadUrl,不过参数需要传递$rawfile()
    加载html格式的文本数据
    当开发者不需要加兹安整个页面,只需要加载一些页面片段的时候,可使用Controller的loadData接口来实现快速加载

6.1.3加载html格式的文本数据

当开发者不需要加兹安整个页面,只需要加载一些页面片段的时候,可使用Controller的loadData接口来实现快速加载

Button('loadData').onClick(() => {try {// 点击按钮时,通过loadData,加载HTML格式的文本数据this.controller.loadData("<html><body bgcolor=\"white\">Source:<pre>source</pre></body></html>","text/html","UTF-8");} catch (error) {console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as BusinessError).message}`);}})

同样也可以直接给src字段传递html格式的字符串数据

 htmlStr: string = "data:text/html, <html><body bgcolor=\"white\">Source:<pre>source</pre></body></html>";build() {Column() {// 组件创建时,加载htmlStrWeb({ src: this.htmlStr, controller: this.controller })}}

6.1.4使用resource协议加载本地资源

Button('加载Resource资源').onClick(() => {try {// 通过resource加载resources/rawfile目录下的index1.html文件this.controller.loadUrl('resource://rawfile/index1.html');} catch (error) {console.error(`ErrorCode: ${error.code}, Message: ${error.message}`);}})

6.2加速Web页面的访问

6.2.1预解析和预连接

此方法通过prepareFoePageLoad接口来预解析或者预连接将要加载的页面

//在web组件的onAppear回调中对要加载的页面进行预连接
.onAppear(() => {// 指定第二个参数为true,代表要进行预连接,如果为false该接口只会对网址进行dns预解析// 第三个参数为要预连接socket的个数。最多允许6个。webview.WebviewController.prepareForPageLoad('https://www.example.com/', true, 2);})

该方式仅对url进行DNS解析以及建立tcp,但不会获取主资源子资源

也可以通过intiializeWebEngine接口来提前初始化内核,然后在初始化内核后调用

//在Ability.ets的onCreat中
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {console.log("EntryAbility onCreate");webview.WebviewController.initializeWebEngine();// 预连接时,需要将'https://www.example.com'替换成真实要访问的网站地址。webview.WebviewController.prepareForPageLoad("https://www.example.com/", true, 2);AppStorage.setOrCreate("abilityWant", want);console.log("EntryAbility onCreate done");}

6.2.2预加载

如果能够预测到web组件将要加载的页面或者即将要跳转的页面,就可以通过prefetchPage接口来预加载页面

预加载会提前下载页面所需的资源,包括主资源子资源,避免阻塞页面渲染。但不会执行前端网页的JS代码。
同时prefetchPagewebViewController的方法,需要一个已经关联好web组件的webViewController实例

//在onPageEnd的时候触发下一个要访问的页面的预加载
.onPageEnd(() => {// 预加载https://www.iana.org/help/example-domains。this.webviewController.prefetchPage('https://www.iana.org/help/example-domains');})

6.2.3预获取Post请求

此方法是针对请求级进行优化。通过prefetchResource接口预获取将要加载页面中的post请求。在页面加载结束时,可以通过clearPrefetchedResource接口清除不需要的预获取资源

//在web组件的onApper回调中,对要加载页面中的post请求进行预获取,在onPageEnd回调中清除缓存
.onAppear(() => {// 预获取时,需要将"https://www.example1.com/post?e=f&g=h"替换成真实要访问的网站地址。webview.WebviewController.prefetchResource({url: "https://www.example1.com/post?e=f&g=h",method: "POST",formData: "a=x&b=y",},[{headerKey: "c",headerValue: "z",},],"KeyX", 500);}).onPageEnd(() => {// 清除后续不再使用的预获取资源缓存。webview.WebviewController.clearPrefetchedResource(["KeyX",]);})

6.2.4预编译生成编译缓存

可以通过precompileJavaScript接口在页面加载前生成脚本文件的编译缓存

6.2.5静态资源免拦截缓存

可以通过injectOflineResource在页面加载前提前将图片,html、css文件等静态资源加入到应用的内存缓存中

6.3Web组件在不同的窗口间迁移

web组件能够实现在不同窗口的组件树上进行挂载或移除操作,例如将浏览器的Tab页拖出成独立窗口,或拖入浏览器的另一个窗口

基本原理是,通过BuilderNode创建web的离线节点,并结合自定义占位节点控制web节点的挂载与移除。当从一个组件树上移除并挂载到另一个组件树上时,就完成了web组件在窗口间的迁移

  1. 在common.ets中声明一个储存MyNodeController的Map,并且提供初始化函数,需要在初始化函数中完成BuilderNode的build方法,并加入Map
// 创建Map保存所需要的BuilderNode
let builderNodeMap : Map<string, BuilderNode<[Data]> | undefined> = new Map();
// 创建Map保存所需要的webview.WebviewController
let webControllerMap : Map<string, webview.WebviewController | undefined> = new Map();// 初始化需要UIContext对象,UIContext对象可通过窗口或自定义组件的getUIContext方法获取
export const createNWeb = (url: string, uiContext: UIContext) => {// 创建WebviewControllerlet webController = new webview.WebviewController() ;// 创建BuilderNodelet builderNode : BuilderNode<[Data]> = new BuilderNode(uiContext);// 创建动态Web组件builderNode.build(wrap, new Data(url, webController));// 保存BuilderNodebuilderNodeMap.set(url, builderNode);// 保存WebviewControllerwebControllerMap.set(url, webController);
}
// 自定义获取BuilderNode的接口
export const getBuilderNode = (url : string) : BuilderNode<[Data]> | undefined => {return builderNodeMap.get(url);
}
// 自定义获取WebviewController的接口
export const getWebviewController = (url : string) : webview.WebviewController | undefined => {return webControllerMap.get(url);
}//NodeController类
// 用于控制和反馈对应的NodeContainer上的节点的行为,需要与NodeContainer一起使用
export class MyNodeController extends NodeController {private builderNode: BuilderNode<[Data]> | null | undefined = null;private webController : webview.WebviewController | null | undefined = null;private rootNode : FrameNode | null = null;constructor(builderNode : BuilderNode<[Data]> | undefined, webController : webview.WebviewController | undefined) {super();this.builderNode = builderNode;this.webController = webController;}//....
}
  1. 在EntryAbility中调用初始化方法
createNWeb(defaultUrl, windowStage.getMainWindowSync().getUIContext());
  1. 在@Entry装饰的入口页面中调用获取函数获得Map中目标url对应的MyNodeController
private nodeController : MyNodeController =new MyNodeController(getBuilderNode(defaultUrl), getWebviewController(defaultUrl));
http://www.dtcms.com/a/613551.html

相关文章:

  • 微网站 微信网站优化服务是什么意思
  • VS Code 隐藏顶部标题栏中间的文字
  • 珠海网站哪家好如何给网站流量来源做标记通过在网址后边加问号?
  • Rust入门
  • Rust入门 之一
  • “伪”局域网
  • C语言编译软件Mac | 在Mac上选择最合适的C语言编译工具
  • 怎么样建设一个网上教学网站网页版微信二维码不能直接识别
  • Linux BPF 技术深度解析:从原理到实践
  • 高端网站报价wordpress如何添加背景音乐
  • C# 对多个任务进行符合管理
  • 在Eclipse IDE for Embedded C/C++ Developers软件中定义的宏,编译C源文件时编译器无法找到宏定义!
  • 从局域网到全网可用!PDFMathTranslate 翻译工具的进阶使用法
  • 深入理解 JavaScript 异步编程:从单线程到 Promise 的完整指南
  • 怎么自己做歌曲网站沈阳网站建设方案策划
  • 电脑卡顿因重复文件?AllDup无安装版快速查重+批量删除 文件管理混乱?AllDup多模式查重工具,Python开发者也能高效用
  • Dubbo Mock机制详解:服务降级与本地测试的利器
  • JDBC与事务的协同:ThreadLocal的巧妙运用
  • 底层视觉及图像增强-项目实践理论补充(十六-0-(13):HDR技术全链路解析:从原理到LED显示工程实践):从奥运大屏,到手机小屏,快来挖一挖里面都有什么
  • 深圳服务平台网站网站提示域名解析错误怎么办
  • 论文阅读13——基于大语言模型和视觉模态融合的可解释端到端自动驾驶框架:DriveLLM-V的设计与应用
  • 考研408--数据结构--day2--顺序表及其增删改查
  • 软件演示环境动态扩展与成本优化:基于目标跟踪与计划扩展的AWS Auto Scaling策略
  • 网站设计的资质叫什么花蝴蝶韩国免费视频
  • AI Agent 之工具使用:从函数定义到实际应用
  • 【C++】 map/multimap底层原理与逻辑详解
  • 如何利用国外网站开发客户wordpress的免费模板
  • C++、Java 还是测试开发?
  • Java 开发 - 粘包处理器 - 基于消息头 + 消息体(魔数验证、长度验证)
  • Spring Cloud Data Flow 简介