横須賀第765管区情報局

2019/12/15

Kotlin/JSのバンドルファイルを頑張って1/6に削減した

この記事はKotlin Advent Calendar 2019・16日目の記事です。


先日、第二回技術書同人誌博覧会にて、週刊IM@Study Vol.4という合同誌を頒布させていただきました。こちらに寄稿した記事『小鳥さんと一緒に100%Kotlinのサイト製作 〜frontend編〜』にて、性懲りもなくKotlin/JSネタで一筆したためたのですが、サンプル用のWebアプリを実装した時にハマった箇所がありました。今回はこちらを解決策と共に共有します。

#ハマったこと: バンドルファイルデカすぎ問題

Kotlin/JSを使ってWebアプリを実装する際、大まかな手順は以下のようになります。今回問題が発生したのは、2と3の手順です。

  1. DOM操作をKotlinで実装
  2. KotlinのコードをJavaScriptに変換
  3. 変換後得られたJavaScriptのコードをWebpackで1ファイルにまとめ、minify

Kotlin->JS変換とWebpackのバンドル処理は、npmとGradleのどちらでも実行可能です。今回はフロントエンドエンジニアを想定読者としたため、Kotlin->JS変換にKotlinWebpackPluginというnpmモジュールを使う構成にしています。

そして、実際に実装したWebアプリがこちら。
→765プロスケジュールチェッカー: imasbook04.otonashi-kotlin.dev
→GitHub: subroh0508/imasbook04-sample

見ての通り、一切スタイルをいじらない素朴なWebアプリとなっています。使用しているライブラリも数少なく、出力されるバンドルファイルも大したサイズにはならないと高を括っていたのですが…

図1. productionモードでそのままビルド

何も考えずproductionモードでビルドし、出力されたバンドルファイルのサイズはなんと1.12MB!流石にデカすぎる…、このままで本番環境にデプロイするのはめちゃくちゃ気が引ける…😣

というわけで、このデカすぎバンドルファイルを小さくしていきます。

#(1) kotlin-dce-jsを有効にする

Kotlin/JSには、JS変換時に使われていないメソッドを自動判別し、これらを削除するツール「kotlin-dce-js」が存在します(参考: kotlinlang.org/docs/reference/javascript-dce.html)。これを利用することで、Kotlin/JSの標準ライブラリ、およびKotlin製ライブラリのJS変換後のファイルサイズを大幅に削減することが可能になります。

#参考: kotlin-dce-js適用前後のファイルサイズ比較
  • kotlin.js: 2.1MB -> 274KB
  • kotlinx-html-js.js: 602KB -> 121KB

KotlinWebpackPluginでは、optimizeオプションでこのツールを有効にすることができます。デフォルトの設定ではkotlin_buildというディレクトリにJS変換後のファイルが出力されるので、yarn run webpackの実行後正しく出力されるか確認してみるとよいでしょう。

webpack.config.js
const path = require('path'); module.exports = { ..., plugins: [ new KotlinWebpackPlugin({ src: path.resolve(__dirname, 'src'), optimize: true, // ←ココ moduleName: 'bundlekt', moduleKind: 'commonjs', librariesAutoLookup: true, verbose: true, sourceMap: !isProduction, packagesContents: [require('./package.json')], }), ..., ], };

optimizeはKotlinWebpackPluginのREADME.mdに書いていないオプションなので注意!該当コードはこの辺りです。

さて、kotlin-dce-jsも有効になったところで、再度ビルドを走らせて見ましょう!どのくらい小さくなったかな…!

図2. kotlin-dce-jsを有効にしてビルド

さっきと同じ1.12MB!全然変わってなーい!!!ナンデー???

#(2) kotlin-dce-jsを通した後のJSファイルを優先的に読み込むよう設定を修正

kotlin-dce-jsを通したにも関わらず、バンドルサイズが全く変わらない問題。こちらは、JSモジュールを1つのバンドルファイルにまとめる過程で、kotlin-dce-jsを通す前のファイルを読み込んでしまうことが原因です。これを解決するには、webpack.config.jsに以下の記述を追加します。

webpack.config.js
const path = require('path'); module.exports = { ..., resolve: { modules: [ path.resolve(__dirname, 'kotlin_build'), path.resolve(__dirname, 'node_modules'), ], }, ..., }

こうすることで、Kotlinの標準ライブラリの元データが含まれるnode_modulesディレクトリより先に、kotlin-dce-jsを通したファイルの出力先であるkotlin_buildディレクトリを読み込むよう指定できます(この設定はREADME.mdにしっかり書いてあります、ちゃんと読もうね自分🥺)。

これで準備は整った…!いざ、ビルド実行!!!

図3. モジュールの読み込み順を変更してビルド

1.12MBあったバンドルファイルが185KBまで小さくなりました!大勝利!!!

#余談: Gradleではどうするの?

Kotlin/JSのGradle Pluginでのkotlin-dce-jsサポートはv1.3.70で正式に入るようです。現状でも利用できないことはないはずですが、正直しんどかった記憶…🤔

#まとめ

毎度思うけど、Kotlin/JSはドキュメントが貧弱すぎてつらい

8月のKotlin Fest 2019では、主にGradleを使ったKotlin/JS製Webアプリの実装手順を解説しましたが、今回はnpm + Webpackで色々と頑張ってみました。Gradleとnpm、慣れ親しんだ方を自由に選択できるのはKotlin/JSの良さだと思っているので、もっと多くの人に触って欲しいなと思います。