使用 IR 编译器
Kotlin/JS IR 编译器后端是 Kotlin/JS 的主要创新方向, 并为以后的技术发展探索道路.
Kotlin/JS IR 编译器后端不是从 Kotlin 源代码直接生成 JavaScript 代码, 而是使用一种新方案. Kotlin 源代码首先转换为 Kotlin 中间代码(intermediate representation, IR), 然后再编译为 JavaScript. 对于 Kotlin/JS, 这种方案可以实现更加积极的优化, 并能够改进以前的编译器中出现的许多重要问题, 比如, 生成的代码大小(通过死代码清除), 以及 JavaScript 和 TypeScript 生态环境的交互能力, 等等.
从 Kotlin 1.4.0 开始, 可以通过 Kotlin Multiplatform Gradle 插件使用 IR 编译器后端. 要在你的项目中启用它, 需要在你的 Gradle 构建脚本中, 向 js
函数传递一个编译器类型参数:
IR
对 Kotlin/JS 使用新的 IR 编译器后端.LEGACY
使用旧的编译器后端.BOTH
编译项目时使用新的 IR 编译器以及默认的编译器后端. 主要用于 编写同时兼容于两种后端的库.
编译器类型也可以在 gradle.properties
文件中通过 kotlin.js.compiler=ir
来设置. 但是这个设置会被 build.gradle(.kts)
中的任何设置覆盖.
顶级属性(top-level property)的延迟初始化(Lazy initialization)
为了改善应用程序的启动速度, Kotlin/JS IR 编译器会对顶级属性(top-level property)进行延迟初始化(Lazy initialization). 通过这种方式, 应用程序启动时不会初始化它的代码中的全部顶级属性. 而只会初始化在启动阶段需要的那些顶级属性; 其他属性的初始化会被延迟, 直到使用它们的代码真正被执行时才会生成属性值.
如果由于某些原因你需要(在应用程序启动阶段)提早初始化一个属性, 可以对它标注 @EagerInitialization
注解.
对开发阶段二进制文件进行增量编译
JS IR 编译器提供了 对开发阶段二进制文件的增量编译模式, 可以对开发过程提高速度. 在这种模式下, 编译器会在模型层级缓存 Gradle task compileDevelopmentExecutableKotlinJs
的结果. 在后续的编译中, 对未修改的源代码文件使用缓存的编译结果, 可以使得编译更快完成, 尤其是在对代码进行少量修改的情况.
增量编译是默认启用的. 如果要对开发阶段二进制文件禁用增量编译, 请向项目的 gradle.properties
或 local.properties
文件添加以下设置:
输出模式
你可以选择让 JS IR 编译器在你的项目中如何输出 .js
文件:
对每个模块输出
.js
文件. 默认情况下, JS 编译器的编译结果是对项目的每个模块输出单独的.js
文件.对每个项目输出
.js
文件. 你也可以将整个项目编译为单个.js
文件, 方法是向gradle.properties
添加以下设置:kotlin.js.ir.output.granularity=whole-program // 默认值是 'per-module'对每个文件输出
.js
文件. 你也可以设置更加细粒度的输出, 为每个 Kotlin 文件生成 1 个 JavaScript 文件 (如果 Kotlin 文件包含导出的声明, 则会生成 2 个 JavaScript 文件). 启用这个模式的方法如下:向你的构建文件, 添加
useEsModules()
函数, 支持 ECMAScript 模块:// build.gradle.kts kotlin { js(IR) { useEsModules() // 启用 ES2015 模块 browser() } }或者, 你也可以使用
es2015
编译目标, 在你的项目中支持 ES2015 功能.使用
-Xir-per-file
编译器选项, 或者更新你的gradle.properties
文件, 如下:# gradle.properties kotlin.js.ir.output.granularity=per-file // 默认值是 `per-module`
在产品(Production)模式中对成员名称的极简化(Minification)
Kotlin/JS IR 编译器会使用它的内部信息 关于 你的 Kotlin 类和函数之间的关系, 来实现更加有效的极简化(Minification), 缩短函数, 属性, 和类的名称. 这样可以缩减打包完成的应用程序的大小.
当你使用 产品(Production) 模式构建你的 Kotlin/JS 应用程序时, 会自动应用这样的极简化处理, 并默认启用. 要关闭对成员名称的极简化处理, 请使用 -Xir-minimized-member-names
编译器选项:
预览功能: 生成 TypeScript 声明文件 (d.ts)
Kotlin/JS IR 编译器能够从你的 Kotlin 代码生成 TypeScript 定义. 在开发混合 App(hybrid app)时, JavaScript 工具和 IDE 可以使用这些定义, 来提供代码自动完成, 支持静态分析, 使得在 JavaScript 和 TypeScript 项目中包含 Kotlin 代码变得更加便利.
如果你的项目输出可执行文件 (binaries.executable()
), Kotlin/JS IR 编译器会收集所有标注了 @JsExport
注解的顶级声明, 并自动在一个 .d.ts
文件中生成 TypeScript 定义.
如果你想要生成 TypeScript 定义, 你需要在 Gradle 构建文件中明确进行配置. 请在你的 build.gradle.kts
文件的 js
小节 中添加 generateTypeScriptDefinitions()
. 例如:
这些声明位于 build/js/packages/<package_name>/kotlin
目录中, 与相应的未经 webpack 处理的 JavaScript 代码在一起.
IR 编译器目前的限制
新的 IR 编译器后端的一个重要变化是与默认后端之间 没有二进制兼容性. 使用新的 IR 编译器后端创建的库, 会使用 klib
格式, 在默认后端中将无法使用. 同时, 使用旧编译器创建的库, 就是一个包含 js
文件的 jar
, 也不能在 IR 后端中使用.
如果你希望在你的项目中使用 IR 编译器后端, 那么需要 将所有的 Kotlin 依赖项升级到支持这个新后端的版本. JetBrains 针对 Kotlin/JS 平台, 对 Kotlin 1.4+ 版本发布的库, 已经包含了使用新的 IR 编译器后端所需要的全部 artifact.
如果你是库的开发者, 希望为现在的编译器后端和新的 IR 编译器后端同时提供兼容性, 请阅读 针对 IR 编译器编写库 小节.
与默认后端相比, IR 编译器后端还存在一些差异. 试用新的后端时, 需要知道存在这些可能的问题.
将既有的项目迁移到 IR 编译器
由于两种 Kotlin/JS 编译器的接口签名的不同, 要让你的 Kotlin/JS 代码能由 IR 编译器来编译, 可能需要你修改部分代码. 关于如何将既有的 Kotlin/JS 工程迁移到 IR 编译器, 请参见 Kotlin/JS IR 编译器 迁移向导.
针对 IR 编译器开发向后兼容的库
如果你是库的维护者, 希望同时兼容默认后端和新的 IR 编译器后端, 有一种编译器选择设置可以让你对两种后端都创建 artifact, 因此可以支持下一代 Kotlin 编译器, 同时又对你的既有用户保持兼容性. 这就是所谓的 both
模式, 可以在 gradle.properties
文件中通过 kotlin.js.compiler=both
来启用, 或者, 也可以在 build.gradle(.kts)
文件的 js
代码块之内, 设置为项目独有的选项:
使用 both
模式时, 从你的源代码构建库时, IR 编译器后端和默认编译器后端都会被使用(如同它的名称所指的那样). 也就是说, Kotlin IR 的 klib
文件, 以及默认编译器的 jar
文件都会生成. 当发布到相同的 Maven 路径时, Gradle 会根据使用场景自动选择正确的 artifact – 对旧的编译器是 js
, 对新的编译器是 klib
. 因此对于使用两种编译器后端中的任何一个的项目, 你都可以编译并发布你的库.