Kotlin/JS で JavaScript ライブラリを使用する
概要
Kotlin/JS から JavaScript ライブラリを使用する方法を説明します。 AMD に対応している JavaScript ライブラリを前提としています。 例として Lodash, Vue.js を使います。 作成するコードでは型付けは弱いままとして、JavaScript の柔軟性を活かした例としています。 Kotlin による静的型付けの恩恵は得られません。 静的型付けを活かした書き方は別途記事を書きます。
前回の記事の続きになっているので、必要に応じて参照してください。
ソースコード : Release 2017-07-30-211651 · nosix/kotlin-js · GitHub
目次
確認環境
- IntelliJ IDEA Community 2017.2
- Kotlin 1.1.3-2
- Gradle 3.5
- Groovy 1.4.10
参考情報
- RequireJS API
- RequireJS 公式
- Calling JavaScript from Kotlin - Kotlin Programming Language
- Kotlin から JavaScript を呼ぶ方法
解説
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.gradle
で moduleKind = "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" }