Edited at

Vue.js, TypeScript, webpack環境でバンドル速度を上げる

More than 1 year has passed since last update.

TypeScriptを使うとBabelよりwebpackのバンドル速度が低下しました

 2018-04-04 12.15.55.png

上記のようにバンドル後のサイズが379kb程度だと約8秒かかってます

ちなみにBabelだと以下です

 2018-04-04 12.27.02.png

約2.5秒なので、約5.5秒の差があります

webpackの設定を比べてみると

■ Babelの場合


webpack.config.js

{

test: /\.js$/,
use: 'babel-loader?cacheDirectory',
exclude: /node_modules/
}

Babelは

ES2015をbabel-loaderでES5に変換

しています

■ TypeScriptの場合


webpack.config.js

{

test: /\.ts$/,
use: [
{
loader: 'babel-loader?cacheDirectory'
},
{
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/]
}
}
],
exclude: /node_modules/
}

このようにTypeScriptの場合は、

TypeScriptをts-loaderでES2015に変換

ES2015をbabel-loaderでES5に変換

しているため、ts-loaderの変換の部分が遅くなってる原因だと分かります

今回はts-loaderの速度アップのため、2つの改善を行いました

どちらも簡単に設定できるので、Vue.js + TypeScript + webpackを使ってる人は試してみてください

全体のコードはこちらになります

https://github.com/kurosame/vuejs-boilerplate


ts-loaderを並列実行する

トランスパイルと型チェックを並列実行させます

具体的にはts-loaderはJSに変換するトランスパイルのみを行い、

型チェックはFork TS Checker Webpack Pluginというプラグインで(並列で)行います

https://github.com/Realytics/fork-ts-checker-webpack-plugin

$ yarn add --dev fork-ts-checker-webpack-plugin


webpack.config.js

const ForkTsChecker = require('fork-ts-checker-webpack-plugin')

...

{
test: /\.ts$/,
use: [
{
loader: 'babel-loader?cacheDirectory'
},
{
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true // このオプションを追加
}
}
],
exclude: /node_modules/
}

...

plugins: [
new ForkTsChecker()
]


↓↓↓ビルド結果↓↓↓

 2018-04-05 19.14.45.png

約2秒位速くなりました


キャッシュを使う

2通り方法があります


HardSourceWebpackPlugin

初回のwebpack実行時にnode_modules/.cache/hard-source配下にキャッシュを作り

2回目以降のwebpack実行からはキャッシュを使います

$ yarn add --dev hard-source-webpack-plugin


webpack.config.js

const HardSource = require('hard-source-webpack-plugin')

...

plugins: [
new HardSource()
]


 2018-04-06 15.37.50.png

約3.5秒位速くなりました

Fork TS Checker Webpack PluginとHardSourceWebpackPluginを2つ使うことで

最初の約8秒から約2.5秒まで短縮させることができました

ただし、このプラグインのトラブルシューティングを見てみると

webpackとwebpack-dev-serverで同じwebpack.configを使っていても、それぞれ異なるキャッシュを持つ必要があるみたいです

https://github.com/mzgoddard/hard-source-webpack-plugin#troubleshooting

よってwebpack-dev-serverやNuxt.jsを使っている場合は、別途設定が必要で

webpack-dev-serverの場合は、contentBaseオプションに指定したディレクトリがルートになるので、

以下のような指定が必要になるかもしれません


webpack.config.js

new HardSource({

cacheDirectory: '.cache/hard-source/[confighash]',
})


Cache Loader

HardSourceWebpackPluginと同様に初回実行時にキャッシュを作り、2回目以降の実行からキャッシュを適用します

こちらはloader専用でloader個別に設定できます

$ yarn add --dev cache-loader


webpack.config.js

{

test: /\.ts$/,
use: [
{
loader: 'babel-loader?cacheDirectory'
},
{
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/]
}
}
// ここから追加
{
loader: 'cache-loader',
options: {
// こちらのオプションは無くてもいいですが、デフォルトだとプロジェクトルート直下に
// キャッシュディレクトリを作るので、node_modules配下に作るように変更してます
cacheDirectory: path.resolve(
__dirname,
'node_modules/.cache/cache-loader'
)
}
},
// ここまで
],
exclude: /node_modules/
}

 2018-04-06 16.28.43.png

こちらも約3.5秒位速くなりました

HardSourceWebpackPluginと比べると若干遅いです


どっちを使うか

HardSourceWebpackPluginの方が設定も簡単で、速いっぽいので

基本的にはこちらの方が良いでしょう

ただし、HardSourceWebpackPluginと比べてcache-loaderはキャッシュの影響範囲を指定できるので、

HardSourceWebpackPluginで不具合起きる時は、部分的にキャッシュを導入できるcache-loaderを使った方が良い時もあると思います

https://github.com/webpack-contrib/cache-loader/issues/11

こちらのissuesで言われている通り、HardSourceWebpackPluginとcache-loaderを同時に使うことや

また、cache-loaderをあまり効果が望めないloaderに設定することは

キャッシュの生成コストやメンテナンスコストを増やすことになるだけなので止めましょう


補足

ts-loaderよりawesome-typescript-loaderを使った方が速いみたいですが、

前に試した時にVue.jsだと使えなかったので、ts-loaderを使ってます

https://github.com/s-panferov/awesome-typescript-loader/issues/356

他にも以下を試しましたが、あまり効果が出なかったので省きました

thread-loader

https://github.com/webpack-contrib/thread-loader

HappyPack

https://github.com/amireh/happypack