[Ktjs x React] 前置知识,ktjs 与 js

作者: 何晓杰Dev | 来源:发表于2019-05-19 13:58 被阅读42次

    终于还是决定开这个坑,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.jskotlin-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 了。

    相关文章

      网友评论

        本文标题:[Ktjs x React] 前置知识,ktjs 与 js

        本文链接:https://www.haomeiwen.com/subject/niftzqtx.html