ずっとWebpackのソースコードを読んだり、仕組みを知りたかったのですが、時間がなくてやれずにいました。
今日からはWebpackの仕組みについて、ちょっとずつ探っていこうと思います。何度かに分けて書きますが、記事が溜まってきたらQiitaにでもまとめます。
ソースコードを読む前に、まずは変換後のファイルをざっくりと読んでみます。
内部的にmoduleを通し番号で管理している、、、くらいの知識はありますが。実際にはどうなっているのでしょうか。
環境設定
まずはWebpackを使うための最小構成をつくります。
今回はReactは使わず、ES2015を書くだけにとどめます。
パッケージはこんな感じ。
シンプルですね。
webpackコンフィグ
babelrc
コンパイル
いざ、コンパイルさせます。
書いたファイル
こちらが書いたファイル。
変換後のファイル
こちらが変換後のファイル。
↑のファイルを少しずつ読んでいきます。
全体の構成
構成としては、ざっくりと2つに分かれていそうですね。
前半に関数部分があり、後半は関数に渡す引数部分になっていそうです。
ざっくり書くとこんな感じ。
即時関数ですね。引数でデータを渡しています。
関数側はデータを受け取る前提で書かれており、即時関数のためbundle.jsを読んだ時点で実行されます。
まずは、後半の引数部分を見てみましょう。
引数は要素が1つの配列ですね。前半の関数の引数の名前がmodulesとなっている通り、これは複数のモジュールを格納する配列ですね。
配列の要素は、関数になっています。引数はmoduleとexportsの2つ。
moduleをconsole.log()で見てみると、
{ exports: {}, id: 0, loaded: false }
とのこと。
exportsは
{}
と空のモジュールですね。
なるほど。
次に前半の関数部分について見ていきましょう。
ご丁寧に各行にコメントが書かれていますね。
構成としては、まず変数定義があり、次に関数定義があり、そのあとに関数に対してプロパティを追加して、関数をreturnしているみたいです。
まずはこちら。変数定義部分。
空オブジェクトを定義していますね。モジュールキャッシュとコメントにあります。なるほど。モジュールのキャッシュとは…?
今回はスルーします。
次に関数定義部分。ここがbundle.jsの中でも一番行数が多いですね。
moduleIdを受け取ってます。おそらく、モジュールの管理番号ですね。
後半部分で渡しているモジュールの配列と対応していそうです。
まずはじめに、モジュールがキャッシュされているかどうかを見ています。キャッシュされている場合は、キャッシュされているモジュールのexportsプロパティをreturnしてこの関数は終了ですね。
もしキャッシュされていなければ、処理はモジュールの作成へと続きます。
新しいモジュールは、moduleIdをキーとしてモジュールのキャッシュオブジェクトへと格納されます。この処理をwebpackはキャッシュと呼んでいるようですね。
モジュールの中身としては、3つのプロパティがあり、exports, id, loadedです。
exportsには空オブジェクトが、idにはmoduleIdが、loadedにはfalseがセットされます。moduleId以外は固定ですね。
モジュールの作成後、関数の実行が行われます。引数で受け取ったモジュールのうちでmodules[moduleId]がFunction.prototype.callによって実行されます。
callの引数としては、まずはコンテキストとして先ほど作成したmoduleのexportsプロパティが渡されます。空オブジェクトですね。
そして、第二〜四引数として、module, module.exports, __webpack_require__が渡されています。
先ほどconsole.logで見たのが、moduleとmodule.exportsでした。あれ、でも__webpack_require__はなぜ渡しているんでしょうね。後ほど明らかになるのかな?
そして、実行が終わるとmoduleのloaded引数がtrueになります。
最後に、module.exportsをreturnしてこの__webpack_require__関数は終了します。
module.exportsって、いわゆるNode.jsの標準実装のものではなく、ここではwebpackが独自に定義しているんですね。ふむ。
その後は、__webpack_require__に拡張が行われます。
mプロパティには引数で渡されたすべてのモジュールが、cプロパティにはキャッシュされているモジュールが、pプロパティには空文字がセットされています。
pプロパティに関しては、public_pathというものが渡されるようです。今回空文字なのは、おそらくwebpack.config側でそういう指定をしていないからだと思われます。
なるほど、各モジュールは、__webpack_require__のプロパティを通じてその他のすべてのモジュールを知ることができるんですね。
そして、前半の関数部分は__webpack_require__の実行をreturnして終了します。
この結果返るのは、moduleのexportsプロパティですね。
なるほど。
webpack変換後のコードが少しだけ理解できました。次回はimport/exportを使った場合の変換結果を見てみたいと思います。