Kotlin/JS で JavaScript ライブラリを使用する

概要

Kotlin/JS から JavaScript ライブラリを使用する方法を説明します。 AMD に対応している JavaScript ライブラリを前提としています。 例として Lodash, Vue.js を使います。 作成するコードでは型付けは弱いままとして、JavaScript の柔軟性を活かした例としています。 Kotlin による静的型付けの恩恵は得られません。 静的型付けを活かした書き方は別途記事を書きます。

前回の記事の続きになっているので、必要に応じて参照してください。

ソースコード : Release 2017-07-30-211651 · nosix/kotlin-js · GitHub

nosix.hatenablog.com

目次

確認環境

  • IntelliJ IDEA Community 2017.2
  • Kotlin 1.1.3-2
  • Gradle 3.5
    • Groovy 1.4.10

参考情報

解説

JavaScript ライブラリを使用する

前回の記事では、 RequireJS を使用して JavaScript の依存関係を管理できるようにした。 RequireJS は AMD に対応している JavaScript ライブラリを管理するだけでなく、AMD に対応していないライブラリも管理できる。

本記事では、AMD に対応していないライブラリについては記載しない。 AMD に対応していないライブラリでは試行していないためである。 RequireJS にて AMD に対応していないライブラリを使用する場合には config で shim を使えば良いらしい。 (参照 : RequireJS使い方メモ - Qiita)

以降では、AMD に対応しているライブラリについて説明する。

JavaScript ライブラリを CDN 経由で使用する

ライブラリをダウンロードせず、CDN (Content Delivery Network) を使用して手軽に試したい場合がある。

例えば、Lodash を使う場合、以下のサイトで配信されており、URL が記載されている。

lodash free CDN links by jsDelivr - A super-fast CDN for developers and webmasters

RequireJS で使用するには、前回の記事で作成した require-config.js に上記サイトに記載されている URL を追記すればよい。 以下は require-config.js の内容である。

var require = {
    baseUrl: 'build/js',
    // 追加
    paths: {
        lodash: 'https://cdn.jsdelivr.net/lodash/4.17.4/lodash', // 拡張子の.jsは不要
    },
    // ^^^
    enforceDefine: true,
};

次に、Kotlin/JS のコードを記述する。 型チェックが弱いままで使用する方法である。 JavaScript のコードをほぼそのまま記述するため記述の手間は少ないが、型チェックや補完機能の恩恵を得られない方法である。

SampleClient.kt を以下に変更する。

external val lodash: dynamic

fun run() {
    println(lodash.capitalize("hello world"))
}

external val lodash: dynamic とすると、JavaScript の lodash 変数を参照できる。 external は外部、すなわち JavaScript のコードを示す。 dynamic は変数の型が動的であることを示す。

ビルドで生成される JavaScript のコードは以下になる。(一部抜粋)

  function run() {
    println(lodash.capitalize('hello world'));
  }

このコードでは lodash 変数が見つからずにエラーとなる。 また、生成された sample-client.js では lodash への依存関係が定義されない。

エラーを解決するために SampleClient.kt を以下に変更する。

@JsNonModule
@JsModule("lodash")
external val lodash: dynamic

fun run() {
    println(lodash.capitalize("hello world"))
}

依存関係を定義させるためには @JsModule を使用して、モジュール名(ライブラリ名)を指定する。

@JsModule を使用しており、かつ build.gradlemoduleKind = "plain"moduleKind = "umd" とした場合には、ビルドエラーとなる。 モジュールシステムを使用しない場合のために @JsNonModule を指定する必要がある。

前回の記事に引き続き moduleKind = "umd" としているため、 @JsModule@JsNonModule を指定している。

@JsModule を指定した場合には、生成される JavaScript コードは以下になる。(一部抜粋)

  function run() {
    println($module$lodash.capitalize('hello world'));
  }

以上でエラーは解消され、ブラウザのコンソールに Hello world が表示される。

Lodash を使う場合の注意点

Lodash を moduleKind = "plain" で使う場合には注意が必要である。 Lodash や Underscore.js では Global スコープの _ を使用する。

そのため、SampleClient.kt のコードは以下にする。

external val `_`: dynamic

fun run() {
    println(`_`.capitalize("hello world"))
}

Kotlin/JS が生成するコードでは、Closure スコープに _ を使用する。 このため、Global スコープの _ を隠蔽してしまいエラーとなる。

sample-client.js を読み込む前に _lodash に代入して、変数名も lodash にすれば対応できる。 しかし、モジュールシステムを使用した方が簡潔である。

JavaScript ライブラリをダウンロードして使用する

Gradle では JavaScript ライブラリとの依存関係定義のために WebJars を使用することができる。 前回の記事でも RequireJS との依存関係定義に使用した。

例として、Vue.js の依存関係を追加する。

sample-client/build.gradle に以下を追加する。

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
    compile "com.example:sample:1.0-SNAPSHOT"
    compile 'org.webjars:requirejs:2.3.3'
    compile 'org.webjars:vue:2.1.3' // 追加
}

次に、Vue.js の使い方に従い sample-client/index.html に以下を追記する。

<div id="example">{{ message }}</div>

message を表示するように SampleClient.kt を変更する。

@JsNonModule
@JsModule("vue")
external class Vue(option: Json)

fun run() {
    val vm = Vue(json(
            "el" to "#example",
            "data" to json(
                    "message" to "Hello World"
            )
    ))
}

比較のため JavaScript で記述する場合を記載しておく。

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello World'
  }
})

Kotlin と JavaScript の対応は以下になっている。

Kotlin JavaScript
Vue() new Vue()
json() {}
"key" to value key: value

Lodash の時との違いとしては、external val Vue: dynamic ではなく external class Vue(option: Json) としている。 Vue.js の場合にはモジュールオブジェクトは関数オブジェクトになっており、その関数はインスタンス生成に使用される。 val Vue とした上で Vue(...) とすると関数実行になるが、 class Vue とした上で Vue(...) とすればインスタンス生成になる。

client-sample/index.html をブラウザで表示すれば、Hello World と表示される。

しかし、vm 変数の型は dynamic ではなく Vue となる。 これにより、Vue クラスに定義されていないプロパティを参照できなくなる。 例えば、Vue.js では JavaScript の場合には以下のように書くことができる。

var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello World'
  }
})
vm.message = 'Hello Kotlin World' 

Kotlin/JS では class Vue にプロパティを定義しなければならない。 しかし、以下の様に dynamic 型を使えばプロパティに定義しなくとも参照できる。

fun run() {
    val vm: dynamic = Vue(json(
            "el" to "#example",
            "data" to json(
                    "message" to "Hello World"
            )
    ))
    vm.message = "Hello Kotlin World"
}

ソースコード

Release 2017-07-31-193534 · nosix/kotlin-js · GitHub