webpack4のTree Shakingを基本的なimport/exportで試してみる

webpack4のTree Shakingを基本的なimport/exportで試してみる:

今まで大して意識をせずにimportexportを行ってきましたが、TypeScriptについていろいろ調べていたところ、このようなものを見つけ。。

export default considered harmful

え、そんな危険なの?と。なんとなく、import書くの楽だし使っていたのですが、ビビりました。まぁ読んでみるとそこまで害悪でもなかったのですが、調べているとアンチパターンについて言及があったりなど、何も考えずに使っていると無駄にbundleされたファイルが大きくなってしまうようなので、ここらでTree-Shakingをテストすることにします。


テスト環境

めっちゃありがたい先駆者の記事がありましたので、そのソースコードをフォークしました。ありがたや。

今回のソースコードはこちらになります。

環境は以下のような感じ。

package.json
"devDependencies": { 
    "babel-core": "^6.26.3", 
    "babel-loader": "^7.1.4", 
    "babel-plugin-transform-imports": "^1.5.0", 
    "babel-preset-env": "^1.6.1", 
    "webpack": "^4.14.0", 
    "webpack-cli": "^3.0.2" 
  } 
webpackは4系で試しています。3系でも試したところ、結構結果変わったので、注意してください。


テストしたもの

かなりバリエーションに富んだ、import & export & default を試しました。以下の「単体export」はexport func, export func2のようにexportだけを使ったもの、「単体でimport」はimport { func }、「全体でimport」はimport * as hogehoge.funcを実行といった感じです。詳しくは、ソースコード見てください。

  • 「単体export」を「単体でimport」: solo.js
  • 「単体export」を「全体でimport」: all.js
  • 「単体exportをそのまま全体でexportしたファイル」を「全体でimport」: all2.js
  • 「単体exportを全体でimportし、それを単体exportしたファイル」を「単体でimport」: all3.js
  • 「export default した object を default as で受けたものを export したファイル」を「単体でimport」: all4.js
  • 「object を export default」を「普通にimport」: object.js
  • 「object を export default」を「default as で import」: object2.js

  • 「アンチパターン検証記事で試されていたもの」を「普通にimport」: object3.js
基本的に、各ファイルには「A」と「B」の2つの関数が入っています。index.jsでは、importしたもののうち「A」が付くものだけを実行しています。

// all.js 
export const allA = () => console.log('allA'); 
 
export const allB = () => console.log('allB'); 
 
// index.js 
import * as all from './all'; 
 
all.allA(); 
つまり、上記の例では、console.log('allA');のみ見つかれば「Tree Shakingが効いている」、console.log('allB');も見つかると「Tree Shakingが効いていない」と判断できます。


実行結果

yarn buildでビルドできます。実行したときの結果は以下の通り。

画像

dist/bundle.js
// 上の方は省略 
  var r = function () { 
      return console.log("soloA") 
    }, 
    l = function () { 
      return console.log("all3A") 
    }, 
    u = function () { 
      return console.log("all3B") 
    }, 
    c = function () { 
      return console.log("all4A") 
    }, 
    f = function () { 
      return console.log("objectA") 
    }, 
    i = function () { 
      return console.log("object2A") 
    }; 
  r(), console.log("allA"), console.log("all2A"), r(), t.all3A(), c(), f(), i(), console.log("foo") 
}]); 
お、all3.js以外はTree Shakingが効いていることがわかりました。


テストからわかったこと1:オブジェクトでexportしてもTree Shakingは効く

アンチパターン検証記事によれば、export default objectではTree Shakingが効かないとありましたが、webpack4では効くようです。(※webpack3では、効かないですが!詳細は後述。)


テストからわかったこと2:全体指定でexportしてもTree Shakingは効く

個人的に気になっていたこととして、「import * as all from './all';として、その一部分しか使用しなかった場合に、Tree Shakingが効くのか否か」があります。

結果としては、Tree Shaking は効いていました。こうなってくると、多量にexportが存在するファイルでは、*でimportしてそこから呼び出した方が気持ちよくなりますね。


テストからわかったこと3:ワンクッションおいてexportしたい場合はexport { default as name }を使う

今回の実験の目的として、これがありました。ReactとReduxのファイル構成パターンであるre-ducksなどで構成が深くなる場合、どうしてもindex.jsにexportを集約させたほうが楽になります。そうした、1クッションおく場合の実装はどのようなimport/exportが最適なのかを知りたかったのですが、それがわかりました。

以下の通りにやると、しっかりTree Shakingが効くことがわかりました。

// all4.js 
export { default as all4 } from './forAll4'; 
 
// forAll4.js 
const all4A = () => console.log('all4A'); 
 
const all4B = () => console.log('all4B'); 
 
export default { all4A, all4B }; 
ちなみに、構文的にexport * as all4 from './forAll3などのようなことはできないみたいなので、これを実現したい場合は上記のようにexport defaultを使う必要があります。


webpack 3系で試すと全然違った

かなり違いました。3系では、アンチパターン検証記事が正しく、オブジェクトの場合は全くTree Shakingが効きません。object.js、object2.js、object3.jsが全滅でした。また同様に、all3.jsとall4.jsでもTree Shakingは効いていませんでした。

3系でも効くのは、solo.jsとall.js、all2.jsのみでした。つまり、単体でimport/exportするか、直接import * as allなどのようにimportする場合は、Tree Shakingが効くということです。

自分で確認したい方は、webpack3用にブランチを切ったので、そちらをクローンし、ビルドしてください。その結果をここでminifyすると、何がTree Shakingされるかが確認できます。


結論

圧縮率も違うみたいだし、webpack3からwebpack4に移行しましょう!!

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)