终于还是决定开这个坑,Kotlin 和 React 都是我非常喜欢的技术,之前也看到了 Kotlin 官方提供了针对 React 的工程向导,体验过一把后直接入坑。
在入坑之前,还是决定先写这么一篇,把 ktjs 讲一番,以便后续可以更好的理解 ktjs + react,同时也是由于大家对 ktjs 都不太了解,也正好是时候扫扫盲,毕竟这么强大的东西只拿来写 Android 实在是太可惜了。
一、建立项目
在 IntelliJ IDEA 的 新建向导内,可以选择 Kotlin/JS
来建立一个基于 Kotlin 的 Js 项目:
填写完基本的要素后,项目就建好了。
接下来要修改一下 build.gradle
,不得不再说一句,JetBrains 做插件真的每次都不做完善,总得改一大堆东西,每次我都忍不住重写各种生成工具,以下是修改后的 build.gradle
。
plugins{
id 'kotlin2js' version '1.3.20'
}
group 'com.rarnu' // 此处改成你自己的 group
version '1.0' // 此处改你自己的版本号
repositories {
mavenCentral()
jcenter()
google()
}
sourceSets.main {
kotlin.srcDirs += 'src/main/kotlin'
resources.srcDirs += 'src/main/resources'
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-js"
compile "org.jetbrains.kotlinx:kotlinx-html-js:0.6.9"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.2.0"
compile "org.jetbrains.kotlinx:kotlinx-io-js:0.1.8"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-io-js:0.1.8"
testImplementation "org.jetbrains.kotlin:kotlin-test-js"
}
compileKotlin2Js {
kotlinOptions.outputFile = "${projectDir}/web/scripts/main.js"
kotlinOptions.moduleKind = "umd"
kotlinOptions.sourceMap = true
}
build.doLast() {
configurations.compile.each { File file ->
copy {
includeEmptyDirs = false
from zipTree(file.absolutePath)
into "${projectDir}/web/lib"
include { fileTreeElement ->
def path = fileTreeElement.path
path.endsWith(".js") && (path.startsWith("META-INF/resources/") || !path.startsWith("META-INF/"))
}
}
}
copy {
includeEmptyDirs = false
from new File("src/main/resources")
into "web"
}
}
需要注意的是,这里我加了一个 build.doLast()
,它指出了在编译完成后,需要拷贝文件到指定目录,因为我们最终的目的还是让项目跑起来,它需要一个完整的 web 项目的结构。
二、使用 ktjs 来生成页面
下面来新建一个页面,命名为 Main.kt
,内容如下:
import kotlinx.html.button
import kotlinx.html.dom.create
import kotlinx.html.h1
import kotlinx.html.js.div
import kotlinx.html.js.onClickFunction
import kotlin.browser.document
@JsName("main")
fun main(args: Array<String>) {
val root = document.getElementById("root")
val div = document.create.div {
h1 { text("Hello Ktjs") }
button {
text("Click Me!")
onClickFunction = { println("Clicked!!!") }
}
}
root?.append(div)
}
从字面意思就很容易理解这段代码做了什么,首先找到页面内的 root
元素,然后向该元素内添加一个 div
,该 div 内包含一个 h1
和一个 button
。需要注意的是,Ktjs 和通常的 Kotlin 程序一样,以 main
函数作为程序的开始,当加载到这个 js 的时候,即会主动执行 main 函数。如果不需要执行 main 函数,可以将该函数置空。
在这里也能够清晰的看到,在生成元素时,可以直接绑定它的 onClick
事件。
或许你会不理解,在等号后面的代码段是怎么来的,为什么可以这么写,其实 onClickFunction
的声明是这样的:
var CommonAttributeGroupFacade.onClickFunction : (Event) -> Unit
所以你可以直接在这里接一个符合声明的函数,这里省略了 Event
参数,按正规的 js 写法应当是:
onClickFunction = { event ->println("Clicked!!!") }
在不声明 event
的情况下,这个参数会被 Kotlin 默认为 it
,所以你也可以把代码改成这样:
onClickFunction = { println("Clicked, $it") }
好了,现在写一个简单的页面,命名为 index.html
并放到 resources
目录内:
<html>
<body>
<div id="root"></div>
<script type="text/javascript" src="./lib/kotlin.js"></script>
<script type="text/javascript" src="./lib/kotlinx-html-js.js"></script>
<script type="text/javascript" src="./scripts/main.js"></script>
</body>
</html>
编译运行吧,可以看到页面已经成功生成了:
运行项目三、操作页面元素
上面的例子允许你在 Kotlin 内直接编写页面,然而对于复杂的页面来说,这么做无疑是浪费时间,我们有更简单的方法可以写出 html,更多的场景是用 js 直接操作页面元素,比如说为按钮绑定事件。
下面来看一个例子,首先还是 html 页面,为了方便起见,只写一个按钮:
<button id='btn'>Sample</button>
随后为它绑定一个 onClick 事件:
@JsName("main")
fun main(args: Array<String>) {
val btn = document.getElementById("btn").asDynamic()
btn.onclick = { println("button clicked!") }
}
此时在页面中的按钮就拥有了点击事件。同样的,我们也可以为页面中的元素设置各种属性,动态的修改页面的样式等。
四、Ktjs 导出
导出是为了让普通的 js 可以使用 Ktjs 写出来的函数,为了能正常的加载 Ktjs 的模块,我们需要引入 require.js
:
<script type="text/javascript" src="./lib/require.js"></script>
要在编译时自动带上 require.js
,可以将该 js 放到项目的 resources/lib
下,编译后该 js 即可以自动被拷贝到 web/lib
内。
Ktjs 可以导出类以及函数,例如以下类定义和函数定义:
class SampleClass(val name: String){
fun use() { println(name) }
}
@JsName("sayHello")
fun sayHello(name: String) = "Hello, $name"
需要注意的是,Ktjs 可以直接导出类,只要声明就可以,而导出函数时,必须加上 @JsName
注解以指出导出名称,否则编译器会在函数名后加上一个数字后缀,对调用函数造成影响。当然了,系统的做法是为了防止函数名称重复,所以我们也需要在写代码时注意命名的问题。
导出后可以在 js 代码内使用导出的类或函数:
<script type="text/javascript" src="./lib/require.js"></script>
<script type="text/javascript">
requirejs.config({
paths: {
'kotlin': 'lib/kotlin',
'kotlinx-html-js': 'lib/kotlinx-html-js',
'main': 'scripts/main'
}
});
requirejs(["main"], function (module) {
var sample = new module.SampleClass("rarnu");
sample.use();
module. sayHello("ktjs");
});
</script>
此处使用 require.js
来加载模块,就不再需要直接引入 kotlin.js
和 kotlin-html-js.js
了,全部让 require.js
来管理。加载完毕后的回调函数内,module
对象就是被加载的模块,可以直接从中获取对象。
五、Ktjs 调用 js
相比 js 调用 ktjs,反过来的操作太简单了,ktjs 内提供了一个 js()
函数,可以直接执行 js 代码。假设在 html 内有如下的 js 方法定义:
<script type="text/javascript">
function jsFunc() {
console.log("js func");
return 0;
}
</script>
则在 ktjs 只需以下代码就可以调用了:
val ret = js("jsFunc();")
println(ret) // 此处得到 0
前置的知识到此已经全部讲完了,我上传了一个 Demo 程序(点此进入)供各位参考,后面再写就是真正的 Ktjs x React 了。
网友评论