目前 js 单文件超过40万行后,鸿蒙编译会报错,错误如下:
> hvigor ERROR: Failed :entry:default@CompileArkTS...
> hvigor ERROR: 10311009 ArkTS: ERROR
Error Message: Failed to execute es2abc.
* Try the following:
> Please refer to es2abc's error codes.
这个问题已经反馈给鸿蒙团队了,但是什么时候解决还未知。目前,为了解决这个问题,我们需要拆包,防止单文件超过40万行。
Kotlin编译成js 文件时,默认一个module 生成一个js 文件。想要降低文件大小,有两个方法:
-
开启编译选项,使得每个kt文件生成一个js 文件 这条路会遇到kotlin标准库生成的js存在循环依赖的bug, 这里不展开阐述,感兴趣的朋友可以自己试一下
-
将一个大module拆成多个小module 我们将选取这条路,module 不支持循环依赖,因此 module -> js file, 依赖关系的映射天然同步。
如何拆module这里就不过多赘述了,大家依照自己的项目情况按需拆即可。这里主要讨论拆完module后,多module编译成js的问题:
假设有两个module, 分别为A和B,A依赖B。
当编译A时,会产生 A.d.ts, A.js 以及B.js 文件。
不知道细心的你有没有发现,少了B.d.ts文件!
实际上B.d.ts 的内容被合并到了A.d.ts中了。这样会导致一个问题: 如果 import func from 'A.d.ts', 但是这个func 实际是在B.js 中实现的(也是就在module B中), 运行时是拿不到func的,因此func 是undefined,后续调用它会crash。
为了解决这个问题,我们需要从A.d.ts中删除B.d.ts中的内容,并且需要保留B.d.ts文件。如何做呢? 我们可以先编译module B, 这样会得到B.d.ts, 再编译module A, 这样就得到了A.d.ts和B.d.ts,然后,我们从A.d.ts中删除B.d.ts的内容。
问题的关键来到了如何从A.d.ts 中删除B.d.ts的内容。这一步我们可以利用 ts-morph 库,将文件解析生AST,修改后再写入文件。
当文件内容只有五六千行,这么做是没问题的,但是d.ts当文件是五六万行,AST的修改会变得非常慢,处理需要十几分钟。这显然是无法接受的,基本无法在日常工作中使用。所以我们来到了最后一步,优化AST速度。
AST 修改速度优化
AST 修改慢的原因是,我们每次remove,都在操作AST树,涉及这颗树的部分重建以及信息维护。那为了解决这个问题,我们可以不修改AST树,通过字符串替换的方式去删除我们的目标内容。实测下来,用字符串的方式删除,比直接操作AST快300倍
// 用字符串的方式删除,比直接删除树的节点快300倍
function removeStatementsByString(container, toRemove) {
// 获取原始文本
let text = container.getFullText();
// 先把要删除的语句按位置排序(从后往前删,避免索引错位)
const ranges = toRemove
.map(stmt => ({ start: stmt.getFullStart(), end: stmt.getEnd() }))
.sort((a, b) => b.start - a.start);
for (const r of ranges) {
text = text.slice(0, r.start) + text.slice(r.end);
}
// 用修改后的文本重新设置 sourceFile
container.replaceWithText(text);
}
相关代码已开源,欢迎讨论! github.com/guoxiaojon/…
关于「解决kotlin/js 生成的单文件过大」的介绍就告一段落了,如果大家在使用过程中有任何问题,欢迎留言讨论。 Android工程师的kmp(kotlin/js) for harmony开发指南 这一系列文章旨在系统性的提供一套完整的Kotlin/Js For Harmony的解决方案。后续系列文章会介绍如何复用ViewModel,序列化卡顿优化,鸿蒙开发套件,架构设计思路等等,欢迎关注!