Kotlin/JSでWebフロント開発をはじめよう

こんにちは、マヤミトです。
この記事は[Kotlin Advent Calendar 2020](https://qiita.com/advent-calendar/2020/kotlin) 18日目の記事です。
皆さんはKotlin/JSを使っていますか？まだまだ記事が少ないこともあり、どう始めればいいのかわからないという人もいるかもしれません。今回は、普段Kotlinを書いている人がKotlin/JSでWebフロント開発を始めるのに最低限必要な知識を身につけられることをゴールにこの記事を書いています。解説不足の部分があれば気軽にコメントしてください。

# Kotlin/JSについて
Kotlin/JSは、文字通りKotlinをJavaScriptにトランスパイルする技術です。
https://kotlinlang.org/docs/reference/js-overview.html
当然JVMの資産は使えませんが、その代わりにDOMの操作などをKotlinでType Safeに扱うことができます。
NodeJSの資産を使ってサーバーサイドの開発もできますが、今回の記事では特に触れないことにします(興味のある方は以前書いた[こちらの記事](https://qiita.com/yt8492/items/1271b30ecd26f5ca0efb)をご覧ください)。

Kotlin/JSを扱う方法は大きく分けて2種類あり、通常のWebフロント開発のようにnpmのプロジェクトとして作成する方法と、通常のKotlinでの開発のようにGradleのプロジェクトとして作成する方法があります。古い記事ではnpmを使う方法が多くヒットしますが、最近だとGradle Pluginを使う後者の方法が公式では推奨されており、今回はそちらの方法を解説します。

# 今回使用したバージョンなど
IntelliJ IDEA Ultimate 2020.3
Kotlin 1.4.21
Gradle 6.7

Kotlin/JSは情報が古くなるサイクルが早いので、この記事の通りにやっても動かない場合は最新の情報を公式ドキュメントなどに直接探しに行くことをオススメします(まあその公式ドキュメントもたまに古かったりしますが……)

# 簡単なプロジェクトを作ってみる
## プロジェクトのセットアップ
IntelliJ IDEAの `New Project` の `Gradle` から `Kotlin/JS for browser` を選択し、新規プロジェクトを作成します。
この際、 `Kotlin DSL build script` にチェックを付けることをおすすめします。
![スクリーンショット 2020-12-18 18.05.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270890/05e424f1-f7ba-3678-82f4-14dfce3e64f9.png)

このようなプロジェクトが生成されると思います。
![スクリーンショット 2020-12-18 19.19.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270890/7340998a-c6a8-d487-4aa0-1d3fc6530f9f.png)

```kotlin:main.kt
import kotlinx.browser.document

fun main() {
    document.write("Hello, world!")
}
```

```html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>HelloKotlinJS</title>
    <script src="HelloKotlinJS.js"></script>
</head>
<body>

</body>
</html>
```

```gradle:build.gradle.kts
plugins {
    id("org.jetbrains.kotlin.js") version "1.4.21"
}

group = "com.yt8492"
version = "1.0.0"

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-js"))
}

kotlin {
    js {
        browser {
            webpackTask {
                cssSupport.enabled = true
            }

            runTask {
                cssSupport.enabled = true
            }

            testTask {
                useKarma {
                    useChromeHeadless()
                    webpackConfig.cssSupport.enabled = true
                }
            }
        }
        binaries.executable()
    }
}
```

ページに `Hello, world!` と表示されるだけの簡単なコードです。
デフォルトでは、 `プロジェクト名.js` をHTMLから読み込んでいます。これが実際にビルドして吐き出されるファイル名です。任意のファイル名に設定したい場合、 `build.gradle.kts` に設定を追記します。

```gradle:build.gradle.kts
kotlin {
    js {
        browser {
            webpackTask {
                cssSupport.enabled = true
                outputFileName = "main.js" // 追加
            }

            runTask {
                cssSupport.enabled = true
                outputFileName = "main.js" // 追加
            }

            testTask {
                useKarma {
                    useChromeHeadless()
                    webpackConfig.cssSupport.enabled = true
                    webpackConfig.outputFileName = "main.js" // 追加
                }
            }
        }
        binaries.executable()
    }
}
```

## 実行してみる
プロジェクトルートで以下を実行します。

```
./gradlew browserRun --continuous
```

するとブラウザが立ち上がり、実際にページが表示されると思います。

![スクリーンショット 2020-12-18 19.20.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270890/7f50910a-8222-425f-40fe-57438dd6e7bf.png)

`--continuous` オプションはホットリロードを有効にします。開発中はこのオプションをつけて実行すると便利です。

## ビルドしてみる
プロジェクトルートで以下を実行します。

```
./gradlew browserWebpack
```

`プロジェクトルート/build/distributions/` 以下にトランスパイルされたJavaScriptのファイルと `resources` 以下のファイルが吐き出されるので、 `index.html` をブラウザで開くと、先程と同じページが表示されると思います。

## DOMの操作をしてみる
デフォルトでは単純に `Hello, world!` と表示するだけでしたが、ボタンをクリックしたらテキストが表示されるようなコードを書いてみましょう。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Hello Kotlin/JS</title>
</head>
<body>
<button id="greetButton">Greet</button>
<h1 id="greetText"></h1>
</body>
<script src="main.js"></script>
</html>
```

```kotlin:main.kt
import kotlinx.browser.document
import org.w3c.dom.HTMLButtonElement
import org.w3c.dom.HTMLHeadingElement

fun main() {
    val greetButton = document.getElementById("greetButton") as HTMLButtonElement
    val greetText = document.getElementById("greetText") as HTMLHeadingElement
    greetButton.addEventListener("click", {
        greetText.textContent = "Hello, Kotlin/JS!"
    })
}
```

`Greet` ボタンを押すと、以下のように `Hello, Kotlin/JS!` と表示されると思います。
![スクリーンショット 2020-12-18 22.42.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270890/86009552-bd1f-9283-ff18-d97a254bb3b0.png)

`document.getElementById` で要素を取得して操作しているのがわかると思います。

# Kotlin/JSで使えるライブラリ集
今回の記事の目的はKotlin/JSでの開発に最低限必要な知識を身に着けてもらうことなので、各種ライブラリの使い方はここでは解説しませんが、自分がよく使うライブラリを紹介したいと思います。

## kotlin-react
文字通り、KotlinでReactを扱うことができるようになるラッパーライブラリです。Kotlin/JS向けのJavaScriptラッパーライブラリ集[kotlin-wrappers](https://github.com/JetBrains/kotlin-wrappers)のうちの一つです。
https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-react/README.md
最新版ではReact v17に対応していて、hooksも普通に使えます。
kotlin-wrappersリポジトリには、React Routerのラッパーやstyled-componentsのラッパーなどもあります。それらと組み合わせて使うと良いでしょう。

## Ktor Client
Kotlin/MPP対応のHTTP Clientライブラリです。当然Kotlin/JSでも使うことができます。
https://ktor.io/docs/clients-index.html

## Klock
Kotlin/MPP対応の時間を扱うライブラリです。Kotlin/JVMではJavaのDate型などが使えましたが、当然Kotlin/JSでは使えないので、こういったライブラリを使うと良いでしょう。
https://korlibs.soywiz.com/klock/

# 終わりに
今回はKotlin/JS初心者向けの記事でした。Kotlin好きな皆さんがこの記事を読んでKotlin/JSを始めてくれると嬉しいです。
最近のKotlin界隈ではKMMが盛り上がりを見せていますが、Kotlin/JSも流行るといいなぁ……と思いながら地道に活動をしています。興味のある方はぜひ一度試してみてほしいです。