Kotlin for JavaScript(Vue.js的互调兼容)

Kotlin介绍

基于 JVM 的编程语言 Kotlin

Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains开发。

其主要设计目标:
创建一种兼容 Java 的语言

让它比 Java 更安全,能够静态检测常见的陷阱。如:引用空指针

让它比 Java 更简洁,通过支持 variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation 等实现。

让它比最成熟的竞争对手 Scala 语言更加简单。

What does it look like?

Kotlin for JavaScript

Kotlin1.1版本正式加入了对JavaScript的支持,也就是说我们可以Kotlin进行网页开发,并且Kotlin也支持了与JavaScript的相互操作。众所周知,JavaScript是动态类型的语言,而相对来说,Kotlin和Java都是静态类型的。同时,两者在编译运行也很不一样,Java更偏向与编译型语言,而JavaScript更偏向于解释型语言。所以,在Kotlin完美兼容Java的同时,增加了JavaScript的支持,让人好奇不已。接下来,我们就来谈谈Kotlin是如何支持JavaScript,以及它的一个简单实现。

<!-- 优先加载kotlin.js-->
<script type="text/javascript" src="out/production/KotlinJsSample/lib/kotlin.js"></script>

在Kotlin代码中,如果想要调用JavaScript代码,基本上有两种方式: js() 内联模式和头文件模式。

js()内联JavaScript

我们可以使用 js("...") 函数将一些JavaScript代码直接嵌入到Kotlin代码中。但是,有一点要求,js函数的参数必须是字符串常量。

举个例子:

//Main.kt
fun main(args: Array<String>) {
    val message = "Hello JavaScript"
    js("console.log(message)")
}

对于js()函数里的参数,Kotlin编译器选择了不处理,直接输出。所以,这也就说明了为什么js()函数为什么必须是要常量的原因了。

external修饰符定义头文件

如果你用过内联JavaScript的方式,你就会知道,真的很鸡肋!做点log的工作,还能干嘛???

所以,Kotlin又搞了一个强大一些的方式,通过 external 修饰符定义JavaScript文件头的方式进行引入调用。

举个例子:

//Mian.kt
external fun alert(message: Any?)

fun main(args: Array<String>) {
    val message = "Hello JavaScript"
    alert(message)
}

这种方式下,当你在Kotlin中要使用JavaScript代码之前,必须加上相关的JavaScript头文件声明。这么做的目的,就是为了让Kotlin知道相关JavaScript函数的存在,以保证可以正常的调用到,然后在Kotlin编译器环节,直接使用到JavaScript代码。看上去挺麻烦的,但是总的来说,比内联来的强大。

JavaScript中调用Kotlin

Kotlin编译器会将原生的Kotlin代码转换成相应的JavaScript代码,并且对于原先Kotlin中定义的函数名和变量都不会改变,这意味着在JavaScript中我们可以自由的使用相关的函数和属性。

Kotlin for Vue.js

长话短说,前面介绍这么多,现在直接进入正题了。
针对Kotlin语言,需要先建立Vue的头文件声明,让Kotlin知道Vue的函数存在,大部分实现如下:

// Vue.kt
/**
 * Created by Zyao89 on 2017/6/2.
 */
import org.w3c.dom.HTMLElement

@JsName("Vue")
external class Vue (options: Any?){
    var paramAttributes: Array<Any>
    var template: String
    var replace: Boolean
    var created: VueCallback
    var beforeCompile: VueCallback
    var compiled: VueCallback
    var ready: VueCallback
    var attached: VueCallback
    var detached: VueCallback
    var beforeDestroy: VueCallback
    var destroyed: VueCallback
    var inherit: Boolean
    var mixins: Array<Any>
    var name: String
    var `$el`: HTMLElement
    var `$parent`: Vue
    var `$root`: Vue
    fun `$watch`(expression: String, callback: VueCallback, deep: Boolean?, immediate: Boolean?):Unit
    fun `$get`(expressopn: String): dynamic
    fun `$set`(keypath: String, value: Any): Unit
    fun `$add`(keypath: String, value: Any): Unit
    fun `$delete`(keypath: String):Unit
    fun `$eval`(expression: String): dynamic
    fun `$interpolate`(templateString: String): String
    fun `$log`(keypath: String?): Unit
    fun `$dispatch`(evant: String, args: Any?): Vue
    fun `$broadcast`(evant: String, args: Any?): Vue
    fun `$emit`(event: String, args: Any?): Vue
    fun `$on`(event: String, callback: Any): Vue
    fun `$once`(event: String, callback: Any): Vue
    fun `$off`(event: String?, callback: Any?): Vue
    fun `$appendTo`(element: Any, callback: Any?): Vue
    fun `$prependTo`(element: Any, callback: Any?): Vue
    fun `$before`(element: Any, callback: Any?): Vue
    fun `$after`(element: Any, callback: Any?): Vue
    fun `$remove`(callback: Any?): Vue
    fun `$mount`(element: Any?): Vue
    fun `$destroy`(remove: Boolean?): Unit
    fun `$compile`(element: HTMLElement): VueCallback
    fun `$addChild`(options: Any, constructor: Any?): Vue
    fun _init(options: Any): Unit
    fun _cleanup(): Unit
    companion object {
        var config: VueConfig
        fun extend(options: Any): Vue
        fun directive(id: String, definition: Any?): Unit
        fun filter(id: String, definition: FilterCallback?): Unit
        fun component(id: String, definition: Vue): Unit
        fun transition(id: String, definition: Any?): Unit
        fun partial(id: String, definition: String?): Unit
        fun nextTick(callback: Any): Unit
        fun require(module: String): Unit
        fun use(plugin: Any, args: Any?): Vue
    }
    var `$data`: dynamic
}
@JsName("config")
external class VueConfig {
    var prefix: String
    var debug: Boolean
    var silent: Boolean
    var proto: Boolean
    var interpolate: Boolean
    var async: Boolean
    var delimiters: Array<String>
}
interface ValueCallback {
    fun apply(newValue: Any, oldValue: Any): Unit
}

interface VueCallback {
    fun apply(): Unit
}

interface FilterCallback {
    fun apply(value: Any, begin: Any?, end: Any?): Any
}

Kotlin调用时,是从 main 函数开始的,先创建一个简单的Vue对象,demo如下:

// Hello.kt
var vue:Vue? = null

fun main(args: Array<String>) {
    vue = Vue(json(
            Pair("el", "#app"),
            Pair("data",
                    json(
                            Pair("message", "ABCDEF123456789")
                    )
            ),
            Pair("methods",
                    json(
                            Pair("reverseMessage", {
                                change()
                            })
                    ))
    ))
}

fun change() {
    vue?.let {
        console.log(it.`$data`?.message)
        it.`$data`?.message = it.`$data`.message.split("").reverse().join("")
    }
}

最后就是 index.html 的页面展示了,如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
</head>
<body>
    <h1 id="test"></h1>
    <div id="app">
        {{ message }}
        <button v-on:click="reverseMessage">逆转消息</button>
    </div>
    <a class="cur STRING_BASE" href="javascript:KotlinJS.haha()"><strong>我直接调用Kotlin方法</strong></a>
</body>
<script src="js/app/lib/webjars/vue/2.1.3/vue.min.js"></script>
<script src="js/app/lib/kotlin.js"></script>
<script src="js/app/KotlinJS.js"></script>
</html>

总结

最后,声明一下这只是一个实验性的Demo,具体项目开发还需要深入研究,多多交流。

GitHub源码:zyao89/KotlinJS-Vue


作者:Zyao89;转载请保留此行,谢谢;

个人博客:https://zyao89.cn

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容