HarmonyOS 鸿蒙应用开发(十、第三方开源js库移植适配指南)
在前端和nodejs的世界里,有很多开源的js库,通过npm(NodeJS包管理和分发工具)可以安装使用众多的开源软件包。但是由于OpenHarmony开发框架中的API不完全兼容V8运行时的Build-In API,因此三方js库大都需要适配下才能用。
移植前准备
建议在适配JS三方库前,使用[js-e2e]扫描三方库,检查是否存在node.js/web内置模块的依赖。
js-e2e工具时基于eslint进行封装,可分析出JS库代码对node.js/web浏览器的内置模块、对象的依赖及兼容ES标准版本,使用该工具,可以快速知道该库是否依赖node.js/web内置模块。
如果扫描结果不依赖node.js/web内置模块,那么,这个库将比较轻松地适配。如果大量依赖node.js/web内置组件,这时可能需要fork源库代码,进行侵入式修改,或者再找是否存在更适合的其他三方库。
JS三方库扫描工具介绍
js-e2e是基于eslint进行封装、配置规则,用于分析JS库代码对NodeJS和Web浏览器的内置模块、对象的依赖及兼容ES标准版本的工具,支持检查指定源码目录和指定三方库的兼容性。
1 使用git工具同步js-e2e代码
2 安装npm依赖包
3 安装自定义的eslint输出报告formatter,包含csv、csvsimple、vscode、vscodesimple
4 执行检查命令
注意事项
模块规范
注意将要移植使用的三方js库的模块规范,AMD、CMD、CommonJS、ES Module等模块规范,看你需要的三方库所使用的规范是哪种。
CommonJS规范主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段。使用CommonJS规范进行开发,需要依赖Node.js环境。(例如浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量:module、exports、require、global)
AMD规范(异步模块定义)是 RequireJS 在推广过程中对模块定义的规范化产出,不是JavaScript原生支持。使用AMD规范进行开发时,需要引入RequireJS这个第三方函数库,通过define()来定义模块,采用require()语句来加载模块。
CMD(通用模块定义)是 SeaJS 在推广过程中对模块定义的规范化产出。使用CMD规范进行开发时,需要引入SeaJS这个第三方函数库。在CMD规范中,一个模块就是一个文件,使用define()语句定义模块,使用seajs.use()加载模块。
三者的区别
CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
AMD是异步加载模块,核心是预加载,先对依赖的全部文件进行加载,加载完了再进行处理。
CMD也是异步加载模块,是按需加载。AMD和CMD最大的区别是对依赖模块的执行时机不同,而不是加载时机不同,二者皆为异步加载模块。
AMD是前置依赖,在定义模块的时候就要声明其依赖的模块。
CMD是就近依赖,只有在用到某个模块的时候再去require。
模块(关键字module)
“内部模块”现在称作“命名空间”,“外部模块”现在简称为“模块”。
内部外部为一个相对概念,在ES6和Nodejs中,引入了模块的概念,即一个文件就是一个模块;“内部模块”即当前文件内的模块,“外部模块”即当前文件外的其他模块。
例如我们在TS工程下新建一个ts文件声明一个变量a,在另一个文件同样声明一个变量a,这时候会出现错误信息:
原因:因为该文件内容是对全局可见的。
解决方案:只需要通过import || export引入模块系统即可。
ES6:(TS也适用)
导出模块:export;(默认导出 export default)
导入模块:import;
CommonJS和AMD:
导出模块:module.exports 或 exports;
导入模块:require( );
为了支持CommomJS和AMD的exports,TS提供了export=语法:
ArkTS语言
HarmonyOS的ArkTS语言是一种基于TypeScript开发的语言,它专为HarmonyOS系统开发而设计。ArkTS语言结合了JavaScript的灵活性和TypeScript的严谨性,使得开发者能够快速、高效地开发出高质量的HarmonyOS应用程序。
ArkTS语言使用TypeScript语法,TypeScript是一种由微软开发的JavaScript超集语言,它支持JavaScript的所有语法,但添加了一些新的特性和语法,使开发更加可靠和高效。TypeScript最大的特点是引入了静态类型,开发者可以在编译时发现类型错误,提高代码的可维护性和可读性。
TypeScript基础知识包括基本类型、变量声明、函数、类、接口、泛型等。另外,TypeScript还支持模块化开发,可以使用ES模块规范或者CommonJS规范导入和导出模块。在实际项目开发中,统一指定采用一种模块规范,标准的ES模块规范。虽然AekTS使用了TypeScript的语法,但是也是有区别的,并不完全支持TypeScript的所有特性。
关于从TypeScript到ArkTS的适配规则,参见官方文档:
从TypeScript到ArkTS的适配规则typescript-to-arkts-migration-guide.md · OpenHarmony/docs - Gitee.com
注意在移植三方的js软件时,模块的导入导出是可能需要修改三方库源码的。
鸿蒙(HarmonyOS)的ArkTS(Ark TypeScript)使用的模块规范是ES6模块规范,而不是CommonJS模块规范。
ES6模块规范(也称为ECMAScript 2015模块规范)是一种现代的模块系统,它使用import
和export
关键字来导入和导出模块成员。ES6模块规范支持静态导入和导出,具有更好的树摇(tree shaking)和代码拆分(code splitting)特性,有助于优化应用程序的性能和大小。
相比之下,CommonJS模块规范是一种较旧的模块系统,它使用require
和module.exports
来导入和导出模块成员。CommonJS模块规范主要用于Node.js环境,并且在一些旧的浏览器环境中也有支持。
移植范例
鸿蒙官方的三方库地址:OpenHarmony三方库中心仓
移植很简单,成功移植了几个开源三方js库,jsbn和sm-crypto,并且编写了鸿蒙的arkUI的单元测试, 测试已通过。注意老的一些js库,在ts中使用时,需要d.ts类型声明文件。这个文件可以自己编写,也可以从网址找有没有对应的参考。一些常用的js三方库几乎都有现成的d.ts类型定义文件。可以在这里找下:GitHub - DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.
sm-crypto仓库地址:一缕阳光116/sm-crypto
jsbn仓库地址: 一缕阳光116/jsbn
sm-crypto移植
找到d.ts对应的声明文件并根据需要修改。比如依赖了yyz116/jsbn的大数库。
//index.d.ts
import jsbn from '@yyz116/jsbn';
export interface KeyPairHex {privateKey: string;publicKey: string;
}export interface KeyPairPoint extends KeyPairHex {k: jsbn.BigInteger;x1: jsbn.BigInteger;
}/*** Cipher Mode* - `0`:C1C2C3* - `1`:C1C3C2*/
export type CipherMode = 0 | 1;export namespace sm2 {// TODO Type of parameter of jsbn.BigInteger constructorfunction generateKeyPairHex(): KeyPairHex;function doEncrypt(msg: string | ArrayLike<number>, publicKey: string, cipherMode?: CipherMode): string;function doDecrypt(encryptData: string, privateKey: string, cipherMode?: CipherMode, outputType?: {output?: "string" | "array";}): string;function doSignature(msg: string | number[], privateKey: string, options?: {pointPool?: KeyPairPoint[] | undefined;der?: boolean | undefined;hash?: boolean | undefined;publicKey?: string | undefined;userId?: string | undefined;}): string;function doVerifySignature(msg: string | number[], signHex: string, publicKey: string, options?: {der?: boolean | undefined;hash?: boolean | undefined;userId?: string | undefined;}): boolean;function getPoint(): KeyPairPoint;
}export function sm3(input: string | ArrayLike<number>, hmac?: {key: HexString | ArrayLike<number>;mode?: "hmac";
}): string;// SM4.encrypt() expects UTF8 strings (such as "hello"), while SM4.decrypt() expects hex strings (such as "8d0a1f").
export type HexString = string;
export type UTF8String = string;export interface SM4ModeBase {padding?: "none" | "pkcs#5" | "pkcs#7";mode?: "cbc";iv?: number[] | HexString;
}export interface SM4Mode_StringOutput extends SM4ModeBase {output: "string";
}export interface SM4Mode_ArrayOutput extends SM4ModeBase {output: "array";
}export namespace sm4 {function encrypt(inArray: number[] | UTF8String,key: number[] | HexString,mode?: SM4ModeBase | SM4Mode_StringOutput,): string;function encrypt(inArray: number[] | UTF8String, key: number[] | HexString, mode: SM4Mode_ArrayOutput): number[];function decrypt(inArray: number[] | HexString,key: number[] | HexString,mode?: SM4ModeBase | SM4Mode_StringOutput,): string;function decrypt(inArray: number[] | HexString, key: number[] | HexString, mode: SM4Mode_ArrayOutput): number[];
}
对应的index.js文件,修改如下,es6的模块引入方式:
//index.js
import * as Sm2 from './sm2/index.js';
import {sm3} from './sm3/index.js';
import * as Sm4 from './sm4/index.js';export { Sm2 as sm2 };
export { sm3 };
export { Sm4 as sm4 };
注意引入方式的区别,那个sm3为什么单独的{sm3} 呢?其实它导出的函数模块,而其它的Sm2和Sm4只是命名空间。
sm3.js中的原有的CommonJS模块规范,需要修改,如下:
//sm3.jsimport {sm3 as Sm3, hmac } from '../sm2/sm3'
......
/*
module.exports = function (input, options) {input = typeof input === 'string' ? utf8ToArray(input) : Array.prototype.slice.call(input)if (options) {const mode = options.mode || 'hmac'if (mode !== 'hmac') throw new Error('invalid mode')let key = options.keyif (!key) throw new Error('invalid key')key = typeof key === 'string' ? hexToArray(key) : Array.prototype.slice.call(key)return ArrayToHex(hmac(input, key))}return ArrayToHex(sm3(input))
}
* */
export function sm3 (input, options) {input = typeof input === 'string' ? utf8ToArray(input) : Array.prototype.slice.call(input)if (options) {const mode = options.mode || 'hmac'if (mode !== 'hmac') throw new Error('invalid mode')let key = options.keyif (!key) throw new Error('invalid key')key = typeof key === 'string' ? hexToArray(key) : Array.prototype.slice.call(key)return ArrayToHex(hmac(input, key))}return ArrayToHex(Sm3(input))
}
jsbn使用举例:
import jsbn from '@yyz116/jsbn'
var BigInteger = jsbn.BigInteger;
var bi = new BigInteger('91823918239182398123');
console.log(bi.bitLength()); // 67
sm-crypto使用举例:
import {sm2} from '@yyz116/sm-crypto'
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果encryptData = sm2.doEncrypt(msgArray, publicKey, cipherMode) // 加密结果,输入数组
decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode, {output: 'array'}) // 解密结果,输出数组// 纯签名 + 生成椭圆曲线点
let sigValueHex = sm2.doSignature(msg, privateKey) // 签名
let verifyResult = sm2.doVerifySignature(msg, sigValueHex, publicKey) // 验签结果// 纯签名
let sigValueHex2 = sm2.doSignature(msg, privateKey, {pointPool: [sm2.getPoint(), sm2.getPoint(), sm2.getPoint(), sm2.getPoint()], // 传入事先已生成好的椭圆曲线点,可加快签名速度
}) // 签名
let verifyResult2 = sm2.doVerifySignature(msg, sigValueHex2, publicKey) // 验签结果// 纯签名 + 生成椭圆曲线点 + der编解码
let sigValueHex3 = sm2.doSignature(msg, privateKey, {der: true,
}) // 签名
let verifyResult3 = sm2.doVerifySignature(msg, sigValueHex3, publicKey, {der: true,
}) // 验签结果// 纯签名 + 生成椭圆曲线点 + sm3杂凑
let sigValueHex4 = sm2.doSignature(msg, privateKey, {hash: true,
}) // 签名
let verifyResult4 = sm2.doVerifySignature(msg, sigValueHex4, publicKey, {hash: true,
}) // 验签结果// 纯签名 + 生成椭圆曲线点 + sm3杂凑(不做公钥推导)
let sigValueHex5 = sm2.doSignature(msg, privateKey, {hash: true,publicKey, // 传入公钥的话,可以去掉sm3杂凑中推导公钥的过程,速度会比纯签名 + 生成椭圆曲线点 + sm3杂凑快
})
let verifyResult5 = sm2.doVerifySignature(msg, sigValueHex5, publicKey, {hash: true,publicKey,
})
其他资源
TS模块化规范与命名空间_ts命名空间不需要导入-CSDN博客
【坚果派】JS开源库适配OpenHarmony系列——第一期实操-电子发烧友网
node之sm-crypto模块,浏览器和 Node.js 环境中SM国密算法库-CSDN博客
sm-crypto: JavaScript对SM2、SM3、SM4的支持。
GitHub - JuneAndGreen/sm-crypto: 国密算法js版
TS模块化规范与命名空间_ts命名空间不需要导入-CSDN博客
GitHub - wechat-miniprogram/sm-crypto: miniprogram sm crypto library
鸿蒙HarmonyOS实战-ArkTS语言(基本语法) - 知乎
arkui_napi: Development framework for extending the JS Native Module | 原生模块扩展开发框架
OpenHarmony三方库中心仓
Codelabs: 分享知识与见解,一起探索HarmonyOS的独特魅力。 - Gitee.com
华为集成开发环境IDE DevEco Device Tool下载 | HarmonyOS设备开发
zh-cn/application-dev/quick-start/typescript-to-arkts-migration-guide.md · OpenHarmony/docs - Gitee.com
相关文章:

HarmonyOS 鸿蒙应用开发(十、第三方开源js库移植适配指南)
在前端和nodejs的世界里,有很多开源的js库,通过npm(NodeJS包管理和分发工具)可以安装使用众多的开源软件包。但是由于OpenHarmony开发框架中的API不完全兼容V8运行时的Build-In API,因此三方js库大都需要适配下才能用。 移植前准备 建议在适…...
Docker- chapter 1
note 1: docker 利用 volume 进行 presist data。 eg : compose.yaml: volumes:database: //# named db by self list golbal volumes: docker volume ls # the volumes on the disk inpect someone volume: docker volume inspect m…...

解决IntellIJ Idea内存不足
突然有一天我在IDEA打开两个项目时,发生了报错,说我内存不足,我这电脑内存16G怎么会内存不足。下面是我的解决方案。 IntelliJ IDEA 报告内存不足的原因通常与以下几个因素有关: 项目规模较大:如果您正在开发的项目非…...

【网络技术】【Kali Linux】Nmap嗅探(二)多设备扫描
上期实验博文:(一)简单扫描 一、实验环境 本次实验进行Nmap多设备扫描,实验使用 Kali Linux 虚拟机(扫描端)、Ubuntu 22.04虚拟机(被扫描端1)、Ubuntu 18.04虚拟机(被扫…...

简化版SpringMVC
简化版SpringMVC web.xml xml version"1.0" encoding"UTF-8"?> <web-app version"2.5" xmlns"http://java.sun.com/xml/ns/javaee" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation&quo…...

Java密码校验(正则表达式):密码由这四种元素组成(数字、大写字母、小写字母、特殊字符),且必须包含全部四种元素;密码长度大于等于8个字符。
1. 需求 对用户密码的强度进行校验,要求用户密码达到一定的强度,符合安全性要求。 1.1. 基础版需求 密码必须由字母和数字组成(同时包括数字和数字);密码长度大于等于8个字符。 1.2. 进阶版需求 密码由这四种元素…...
【AMI】2400 环境安装步骤
2400 环境安装步骤 ----------Ubuntu14.4 MDS4.0 加载代码需要勾上Update Installing SPX related packages sudo apt install gcc-multilib mtd-utils:i386 subversion patch patchutils bison sudo apt install libc6-dev libxml-dom-perl zlib1g zlib1g-dev libcurl4-ope…...
AI:124-基于深度学习的人体遮挡物体重建技术
🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…...
23种设计模式之单例模式
目录 什么是单例模式 单例模式的优点 创建单例模式的三大要点 单例模式的实现方式 饿汉模式 懒汉模式 使用场景 什么是单例模式 单例模式是一种创建型设计模式,它的核心思想是保证一个类只有一个实例,并提供一个全局访问点来访问这个实例。 什…...
leetCode 30天
题太难了,就来一个N皇后吧 51. N 皇后 class Solution { private:vector<vector<string>> res;void backtracking(int n, int row, vector<string>& chessboard){if (row n){res.push_back(chessboard);return;}for (int col 0; col<n;…...

vue3+vite+ts 配置commit强制码提交规范配置 commitlint
配置 git 提交时的 commit 信息,统一提交 git 提交规范 安装命令: npm install -g commitizen npm i cz-customizable npm i commitlint/config-conventional commitlint/cli -D 文件配置 根路径创建文件 commitlint.config.js module.exports {// 继承的规…...

PlateUML绘制UML图教程
UML(Unified Modeling Language)是一种通用的建模语言,广泛用于软件开发中对系统进行可视化建模。PlantUML是一款强大的工具,通过简单的文本描述,能够生成UML图,包括类图、时序图、用例图等。PlantUML是一款…...

自然语言处理(NLP)——使用Rasa创建聊天机器人
1 基本概念 1.1 自然语言处理的分类 IR-BOT:检索型问答系统 Task-bot:任务型对话系统 Chitchat-bot:闲聊系统 1.2 任务型对话Task-Bot:task-oriented bot 这张图展示了一个语音对话系统(或聊天机器人)的基本组成部分和它们之间的…...

使用虚拟主机部署多站点
网站目录权限的管理和虚拟主机的配置。 目录权限控制...

Openresty+Lua+Redis实现高性能缓存
一、背景 当我们的程序需要提供较高的并发访问时,往往需要在程序中引入缓存技术,通常都是使用Redis作为缓存,但是要再更进一步提升性能的话,就需要尽可能的减少请求的链路长度,比如可以将访问Redis缓存从Tomcat服务器…...
基于Vue2用keydown、keyup事件实现长按键盘任意键(或组合键)3秒触发自定义事件(以F1键为例)
核心代码 <template></template> <script> export default {created() {//监听长按快捷键addEventListener("keydown", this.keydown);addEventListener("keyup", this.keyup);},destroyed(d) {//移除长按快捷键removeEventListener(&…...

【C#】.net core 6.0 设置根目录下某个文件夹可访问,访问创建的图片等资源
欢迎来到《小5讲堂》 大家好,我是全栈小5。 这是《C#》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点的理解和掌握。…...

报错ValueError: Unknown CUDA arch (8.6) or GPU not supported
文章目录 问题描述解决方案参考文献 问题描述 报错 ValueError: Unknown CUDA arch (8.6) or GPU not supported 本人显卡为 RTX 3060,CUDA 为 10.2,PyTorch 为 1.5 解决方案 修改 C:\Users\Administrator\Envs\test\Lib\site-packages\torch\utils\c…...
Golang 并发 Cond条件变量
Golang 并发 Cond条件变量 背景 编写代码过程中, 通常有主协程和多个子协程进行协作的过程,比如通过 WaitGroup 可以实现当所有子协程完成之后, 主协程再继续执行。 如上的场景是主协程等待子协程达到某个状态再继续运行。 但是反过来怎么…...
linux 下 chrome 无法在设置里面配置代理的解决方法
文章目录 [toc]解决方法查找 chrome 命令路径查看 chrome 启动文件方式一方法二 在 linux 环境下,使用 chrome 没办法像 firefox 一样在设置里面配置代理,打开 chrome 的设置会有下面的内容显示 When running Google Chrome under a supported desktop e…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...