1. HarmonyOS ArkTS Web组件jsbridge
1.1. Web组件引入和调用JS库
关于ts可以调用JS库,可以使用以下几种方式:文档中心:https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-arkweb-kit-V5
1.1.1. 鸿蒙系统H5 JSBridge的优势
相比其他移动操作系统,鸿蒙系统的H5 JSBridge具有以下优势:
易用性:JSBridge提供了一套简单易用的API,使得前端开发人员可以方便地进行调用和交互。
扩展性:开发人员可以根据自己的需求自定义原生模块,提供更多的功能和交互效果。
跨平台支持:鸿蒙系统的JSBridge可以在不同的平台上使用,包括Android、iOS和Web等。
1.1.2. 鸿蒙系统H5 JSBridge的应用场景
鸿蒙系统的H5 JSBridge可以应用于各种场景,包括但不限于:
获取设备信息:开发人员可以使用JSBridge调用原生API获取设备的型号、操作系统版本等信息,以便进行针对性的优化和适配。
调用原生功能:开发人员可以使用JSBridge调用原生功能,例如打开摄像头、获取位置信息等,以实现更多的功能和交互效果。
数据传递和共享:JSBridge可以实现前端和原生之间的数据传递和共享,方便实现数据的同步和共享。
1.2. jsbridge
文档地址:https://ohpm.openharmony.cn/#/cn/detail/@ncc%2Fjsbridge
1.2.1. 简介
随着智能手机的普及,移动应用的需求日益增长。而在开发移动应用时,前端开发人员需要与原生平台进行交互,以实现更多的功能和优化用户体验。鸿蒙系统提供了一个强大的H5 JSBridge工具,使得前端开发人员可以方便地与鸿蒙系统进行通信和交互。
JSBridge是一种将JavaScript代码和原生代码进行桥接的技术。它允许前端开发人员通过JavaScript调用原生代码,实现更多的功能和交互效果。鸿蒙系统的JSBridge提供了一套标准的API,方便开发人员进行调用。
JSBridge 是基于 OpenHarmony Web 组件的 JavaScriptProxy 机制开发的三方库,提供了 bridge 形式的 ArkTS 和 JavaScript 相互调用接口。
通过简单的函数注册和初始化后,便可以使用下面的接口进行 ArkTS 与 JavaScript 的相互调用:
// In ArkTS
jsbridge.post('jsFuncName', param1, param2, ...);
// In Javascript (Html)
jsbridge.call('etsFuncName', param1, param2, ...);
1.2.2. 下载安装
ohpm install @ncc/jsbridge
OpenHarmony ohpm 环境配置等更多内容,请参考如何安装 OpenHarmony ohpm 包
上面接口参数中出现自定义类型声明如下:
type SupportTypes = string | number | boolean;
type SupportMethod = (...params: SupportTypes[]) => void | SupportTypes;
type NotFoundHander = (funcName: string) => void | SupportTypes;
1.2.3. 使用示例
下面一个简单的 Web 页面为例子,展示本库的使用:
import web_webview from '@ohos.web.webview'
import JSBridge from '@ncc/jsbridge'
@Entry
@Component
struct Index {
@State message: string = "Show something"
controller: web_webview.WebviewController = new web_webview.WebviewController()
isReady: boolean = false;
// 创建 JSBridge 实例
jsbridge: JSBridge = new JSBridge(this.controller)
build() {
Column() {
Row() {
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
Text(this.message)
.fontSize(18).fontWeight(700).fontColor('#000000')
}
}.padding({ left: 20, right: 20, bottom: 20, top: 0 }).width('100%')
Row() {
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
Button("Call JS function").width('100%')
.onClick((e) => {
if (this.isReady) {
// 通过 jsbridge.post('xxx', xxx) 调用 js 函数
const str = "My string to call JS";
this.jsbridge.post('changeWebMessage', str);
}
})
}
}
Row() {
// Web 组件
Web({ src: $rawfile("example1.html"), controller: this.controller })
// 此处使用的 onControllerAttached 是 API10 新增,API9 可以选用其它事件或在其它时机进行
.onControllerAttached(() => {
if (!this.isReady) {
// 在 controller 与 web 关联后才能进行 bridge 的初始化
this.jsbridge.initBridge();
// 添加支持的方法
this.jsbridge.register({ 'changeNativeMessage': this.changeNativeMessage });
this.isReady = true;
}
})
}.padding({ left: 20, right: 20, bottom: 20, top: 0 })
.width('100%')
}
}
// 一个本地 ArkTS 函数
changeNativeMessage = (message: string) => {
this.message = message;
}
}
<!-- local.html -->
<!DOCTYPE html>
<html>
<body>
<p>
<input type="text" id="input" value="Message to Native">
</p>
<p>
<button id="button"> Click to call ArkTS funcA() </button>
</p>
<div id="text">This is some message.</div>
<script>
const button = document.getElementById("button");
const input = document.getElementById("input");
const text = document.getElementById("text");
button.addEventListener('click', function() {
// 通过 jsbridge.call('xxx', xxx) 调用 ArkTS 函数
jsbridge.call('changeNativeMessage', input.value);
});
function changeWebMessage(str) {
text.innerHTML = str;
}
</script>
</body>
</html>
在上面的例子中,Web 端和应用端都有一个按钮用于彼此间的调用。
(1)通过 jsbridge.post,应用端调用 Web 端的 js 函数,将 Web 端的 div 内的文本修改为 My string to call JS;
(2)通过 jsbridge.call,Web 端调用应用端的 ArkTS 函数,将 Web 端上方的一行文本修改为 input 输入的内容。
1.2.4. 详细说明
下面一个简单的 Web 页面为例子,展示本库的使用:
1.2.4.1. 引用库
import JSBridge from '@ncc/jsbridge'
实例创建与关联
创建的实例需要与一个 web_webview.WebviewController 关联。
示例:
// 方式一:创建后关联
let jsbridge = new JSBridge();
jsbridge.attachController(webviewController);
// 方式二:创建时关联
let jsbridge = new JSBridge(webviewController);
Bridge初始化/移除
初始化需要在 webviewController 与 Web 组件关联后。初始化会向 WebviewController 注册一个 Bridge Object,用于 Web 端调用本地 ArkTS 函数。
示例如下:
try {
// 传入参数为 Web 侧的 Bridge Object 名
// 下面的示例中,初始化后,Web 侧可以使用下面的方式进行调用
// jsbridgeName.call(xxx, ...)
// Name 为可选参数,默认情况下为 'jsbridge'
jsbridge.initBridge('jsbridgeName');
} catch (error) {
// 如果在 controller 未与 Web 组件关联时调用会抛出错误
console.log(error.message);
}
想要取消这一注册,可以使用下面的接口:
try {
jsbridge.removeBridge();
} catch (error) {
console.log(error.message);
}
注意:每次调用 initBridge 时,都会对 webviewController 进行一次刷新(使用 fresh 接口),因此请确保 initBridge 的调用时机在掌控中,或是它只会被调用一次,以避免造成不可控的影响。
1.2.4.2. 添加/移除可调用的应用侧函数
可以使用 register 函数向 jsbridge 添加支持的函数。该函数可以多次调用。
函数同名时,后注册的会覆盖先注册的。示例如下:
// 下面的注册完成后,我们可以在 Web 侧使用 js 调用这两个方法:
// jsbridgeName.call('functionA', 'this is a string', 1)
// jsbridgeName.call('funcB')
jsbridge.register({
'functionA': functionA,
'funcB': functionB,
});
// 方法可以有任意的参数数量
// 但是传入参数和返回值仅支持 string/number/boolean
// 对 Object 类型的传入/返回需求可以尝试用 JSON 序列化解决
function functionA(param1: string, param2: number) {
// do something
}
function functionB() {
// do something
}
如果注册的是成员函数等需要上下文的函数,会存在上下文(this)丢失的风险。在定义时使用箭头函数或手动绑定实体是一种不错的解决方案:
class Student {
name: string = "Alice";
getName1() {
return this.name;
}
getName2 = () => {
return this.name;
}
}
let stu = new Student;
jsbridge.register({'getName1': stu.getName1.bind(stu)});
jsbridge.register({'getName2': stu.getName2});
移除函数可以使用 unregister 接口:
// 支持传入方法名列表
jsbridge.unregister(['getName1', 'getName2'])
// 也支持只传入一个方法名
jsbridge.unregister('getName1');
1.2.4.3. Web侧调用ArkTS
通过 jsbridge.call,可以从 Web 侧调用已被注册的 ArkTS 函数:
jsbridge.call('etsFuncName', params);
1.2.4.4. (可选)配置回调函数
在 Web 侧使用 jsbridge.call 时,如果调用的函数不被支持,call 会代替的以 notFoundHandler 作为回调函数。
默认情况下,该 handler 会在控制台打印一条: “xxx is not found!”。
可以通过下面的接口进行配置:
jsbridge.setNotFoundHandler(
// 传入的函数必须接收一个 string 参数
// 且返回值类型必须是 void | string | number | boolean
(funcName: string) => {return funcName + ’ is not found!';}
);
应用侧调用JS
JSBridge 还基于 WebviewController 的 runJavaScript 接口封装了 post 函数,用于应用侧调用 Web 侧函数。
// 支持直接传入 JS 代码
jsbridge.postJS("jsFunctionA('param1', 1, 2);");
// 也支持 post('xxx', xxx) 的调用形式
// 使用此接口传入的参数,都会被自动序列化后拼接起来
jsbridge.post('jsFunctionA', string1, number1, number2);
1.2.4.5. JS 异步调用 ArkTS 函数的实现方案
本库基础实现的 jsbridge.call 是同步调用。因此我们在此处给出在 JS 侧进一步封装为基于 Promise 的异步调用的方案:
// 'jsbridge' 需替换为 init 时的 jsbridgeName
// 使用 AsyncJSBridge.callAsync(funcName, ...params) 就可以异步调用 Native 函数
const _JSBridge = self['jsbridge'];
const AsyncJSBridge = {
call: (funcName, ...params) => {
// 同步调用接口
return _JSBridge.call(funcName, ...params);
},
callAsync: (funcName, ...params) => {
// 异步调用接口
return new Promise((resolve, reject) => {
try {
let res = _JSBridge.call(funcName, ...params);
resolve(res);
} catch (error) {
reject(error);
}
});
}
}