# ohos_jsbridge **Repository Path**: eachann_lee/ohos_jsbridge ## Basic Information - **Project Name**: ohos_jsbridge - **Description**: 用于 OpenHarmony Web 开发的 JSBridge 封装。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2023-12-21 - **Last Updated**: 2023-12-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # jsbridge ## 简介 JSBridge 是基于 OpenHarmony Web 组件的 JavaScriptProxy 机制开发的三方库,提供了 bridge 形式的 ArkTS 和 JavaScript 相互调用接口。 通过简单的函数注册和初始化后,便可以使用下面的接口进行 ArkTS 与 JavaScript 的相互调用: ```extendtypescript // In ArkTS jsbridge.post('jsFuncName', param1, param2, ...); // In Javascript (Html) jsbridge.call('etsFuncName', param1, param2, ...); ``` ## 下载安装 ```bash ohpm install @ncc/jsbridge ``` ![](./screenshots/example.gif) OpenHarmony ohpm 环境配置等更多内容,请参考[如何安装 OpenHarmony ohpm 包](https://gitee.com/openharmony-tpc/docs/blob/master/OpenHarmony_har_usage.md) ## 接口列表 | **接口** | 参数 | 功能 | |-------------------------------------|-----------------------------------------------------|--------------------------------------------------------------| | attachController(controller) | controller: web_webview.WebviewController | 关联一个 `web_webview.WebviewController` | | initBridge(jsbridgeName) | jsbridgeName?: string | 初始化JSBridge。初始化后,Web端可以使用jsbridgeName对象。名称参数可选,默认为'jsbridge' | | removeBridge() | 无 | 与初始化对应,将JSBridge从Web端移除。移除后,Web端无法访问到jsbridge对象。 | | setNotFoundHandler(notFoundHandler) | notFoundHandler: NotFoundHander | 设置jsbridge.call找不到目标函数时的回调函数 | | register(methods) | methods: Record | 添加jsbridge.call支持的函数列表 | | unregister(methods) | methods: Record | 将传入的函数列表从 jsbridge.call 的支持列表里移除 | | postJS(jsCommand, callback) | jsCommand: string, callback?: AsyncCallback | 等价于WebviewController.runJavaScript接口,在Web端执行jsCommand | | post(funcName, ...params) | funcName: string, ...params: any | 调用Web端的JS函数。传入的参数会经过JSON.stringify序列化。 | | call(funcName, ...params) | funcName: string, ...params: SupportTypes | Web端调用ArkTS函数使用的接口。以jsbridgeName.call()的方式调用。 | 上面接口参数中出现自定义类型声明如下: ```extendtypescript type SupportTypes = string | number | boolean; type SupportMethod = (...params: SupportTypes[]) => void | SupportTypes; type NotFoundHander = (funcName: string) => void | SupportTypes; ``` ## 使用示例 下面一个简单的 Web 页面为例子,展示本库的使用: ```extendtypescript 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; } } ``` ```html

This is some message.
``` 在上面的例子中,Web 端和应用端都有一个按钮用于彼此间的调用。 - 通过 jsbridge.post,应用端调用 Web 端的 js 函数,将 Web 端的 div 内的文本修改为 My string to call JS; - 通过 jsbridge.call,Web 端调用应用端的 ArkTS 函数,将 Web 端上方的一行文本修改为 input 输入的内容。 ## 详细说明 ### 引用库 ```extendtypescript import JSBridge from '@ncc/jsbridge' ``` ### 实例创建与关联 创建的实例需要与一个 `web_webview.WebviewController` 关联。 示例: ```extendtypescript // 方式一:创建后关联 let jsbridge = new JSBridge(); jsbridge.attachController(webviewController); // 方式二:创建时关联 let jsbridge = new JSBridge(webviewController); ``` ### Bridge初始化/移除 初始化需要在 webviewController 与 Web 组件关联后。初始化会向 WebviewController 注册一个 Bridge Object,用于 Web 端调用本地 ArkTS 函数。 示例如下: ```extendtypescript try { // 传入参数为 Web 侧的 Bridge Object 名 // 下面的示例中,初始化后,Web 侧可以使用下面的方式进行调用 // jsbridgeName.call(xxx, ...) // Name 为可选参数,默认情况下为 'jsbridge' jsbridge.initBridge('jsbridgeName'); } catch (error) { // 如果在 controller 未与 Web 组件关联时调用会抛出错误 console.log(error.message); } ``` 想要取消这一注册,可以使用下面的接口: ```extendtypescript try { jsbridge.removeBridge(); } catch (error) { console.log(error.message); } ``` **注意**:每次调用 initBridge 时,都会对 webviewController 进行一次**刷新**(使用 fresh 接口),因此请确保 initBridge 的调用时机在掌控中,或是它只会被调用一次,以避免造成不可控的影响。 ### 添加/移除可调用的应用侧函数 可以使用 register 函数向 jsbridge 添加支持的函数。该函数可以多次调用。 函数同名时,后注册的会覆盖先注册的。示例如下: ```extendtypescript // 下面的注册完成后,我们可以在 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)丢失的风险。在定义时使用箭头函数或手动绑定实体是一种不错的解决方案: ```extendtypescript 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 接口: ```extendtypescript // 支持传入方法名列表 jsbridge.unregister(['getName1', 'getName2']) // 也支持只传入一个方法名 jsbridge.unregister('getName1'); ``` ### Web侧调用ArkTS 通过 jsbridge.call,可以从 Web 侧调用已被注册的 ArkTS 函数: ```javascript jsbridge.call('etsFuncName', params); ``` ### (可选)配置回调函数 在 Web 侧使用 jsbridge.call 时,如果调用的函数不被支持,call 会代替的以 notFoundHandler 作为回调函数。 默认情况下,该 handler 会在控制台打印一条: "xxx is not found!"。 可以通过下面的接口进行配置: ```extendtypescript jsbridge.setNotFoundHandler( // 传入的函数必须接收一个 string 参数 // 且返回值类型必须是 void | string | number | boolean (funcName: string) => {return funcName + ' is not found!';} ); ``` ### 应用侧调用JS JSBridge 还基于 WebviewController 的 runJavaScript 接口封装了 post 函数,用于应用侧调用 Web 侧函数。 ```extendtypescript // 支持直接传入 JS 代码 jsbridge.postJS("jsFunctionA('param1', 1, 2);"); // 也支持 post('xxx', xxx) 的调用形式 // 使用此接口传入的参数,都会被自动序列化后拼接起来 jsbridge.post('jsFunctionA', string1, number1, number2); ``` ### JS 异步调用 ArkTS 函数的实现方案 本库基础实现的 jsbridge.call 是同步调用。因此我们在此处给出在 JS 侧进一步封装为基于 Promise 的异步调用的方案: ```javascript // '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); } }); } } ``` ## 遗留问题 由于当前 OpenHarmony 的 JavaScriptProxy 机制不支持回调函数、Promise类的参数传递和返回,因此我们无法获取到异步Native函数的返回。如果开发者希望 JS 获取到异步 Native 函数的返回结果,可能需要手动对 Native 函数进行适配。 ## 约束与限制 在下述版本验证通过: DevEco Studio: 4.0 Beta2(4.0.0.400), SDK: API10(4.0.9.6) ## 目录结构 ```text |---- ohos_jsbridge | |---- AppScrope # 示例代码文件夹 | |---- entry # 示例代码文件夹 | |---- screenshots # 截图 | |---- jsbridge # jsbridge库文件夹 | |---- build # jsbridge模块打包后的文件 | |---- src # 模块代码 | |---- main | |---- ets/components # jsbridge核心代码 | |---- index.js # 入口文件 | |---- index.d.ts # 声明文件 | |---- *.json5 # 配置文件 | |---- README.md # 安装使用方法 | |---- README.OpenSource # 开源说明 | |---- CHANGELOG.md # 更新日志 ``` ## 开源协议 本项目基于 [Apache-2.0](./LICENSE) ,请自由地享受和参与开源。