TypeScriptでIsomorphicなCode Splittingをする
以前webpack2でDynamic importsが実装される前にWebpackのCode Splittingについて書きましたが、せっかくTypeScript 2.4でもDynamic Importsが実装されたので(結構経ちますが)TypeScript環境下でいかにしてCode Splittingを実現させるかを書き残しておきます。
webpack.ensureとbabelでハマりがちなこと - banseivlog
前提となる環境
- webpack v3.4.1
- TypeScript v2.4.2
- Isomorphic
環境的にはwebpackでクライアントコードをビルドしてサーバーはtsc or webpackでビルドしてます。
TypeScriptにDynamic Imports入った→入れてみた→Code Splittingしない
結構な人が躓くと思うのですが、大抵の場合requre.ensureあたりをそのままDynamic ImportsにしてもCode Splittingしてくれません。
単純にTS側のトランスパイルの仕組みなのですが、tsc --init
とかでtsconfig.jsonを作ると以下のようになります。
{ "compilerOptions": { "target": "es5", "module": "commonjs", "strict": true, // ... } }
普通に使ってる分には問題はないのですがこれだとDynamic ImportsをTypeScriptは以下のように変換してしまいます
import('./hoge');
Promise.resolve().then(function () { return require('./hoge'); });
というのも"module": "commonjs"
と指定されているので、すべてCommonJS方式で出力されます。
ESNextとmoduleResolution
最終的に欲しいアウトプットは、ESModules部分はcommonjs方式に、Dynamic Imports部分をクライアントサイドは無変換で、サーバーサイドは変換されて欲しい。
そこで新たに設定を追加します。
// サーバー側 { "compilerOptions": { // ... "module": "commonjs", "moduleResolution": "node", // ... } } // クライアント側 { "compilerOptions": { // ... "module": "esnext", "moduleResolution": "node", // ... } }
こんな感じでサーバー側はそのままに、クライアントコード側(Code Splittingしたい方)に"module": "exnext"
と"moduleResolution": "node"
を書き加えてあげます。
そうするとクライアントコード側はmoduleのターゲットがesnext(未来仕様)にしているため、Dynamic Importsは変換されずそのままの状態として残ります。ただそれだけだと今度はESModulesが変換されないため"moduleResolution": "node"
と設定することで、import/export
文はcommonjs(nodejs)方式でoutputされます。
もちろん、上記設定をサーバーとクライアントで分けたのはNode.js側ではimportは動かないので変換をかけています。
こうしてサーバーサイドとクライアントサイドのコードを共有することが出来ました。ちなみにrequire.ensure
時代はわりと力技になっていたのでDynamic Importsが導入できて僕らはまたひとつ幸せになることができました。
多種多様な人が働く環境下でTypeScriptの静的型付けはかなり(安心安全的な意味で)パフォーマンスを出してくれるのかなと個人的に思っているので、引き続きTSさんをよろしくお願いします。