コンパイラかく語りき

import { Fun } from 'programming'

webpackはどのように動いているのか(1)

ずっとWebpackのソースコードを読んだり、仕組みを知りたかったのですが、時間がなくてやれずにいました。

今日からはWebpackの仕組みについて、ちょっとずつ探っていこうと思います。何度かに分けて書きますが、記事が溜まってきたらQiitaにでもまとめます。

 

ソースコードを読む前に、まずは変換後のファイルをざっくりと読んでみます。

内部的にmoduleを通し番号で管理している、、、くらいの知識はありますが。実際にはどうなっているのでしょうか。

 

環境設定

まずはWebpackを使うための最小構成をつくります。

今回はReactは使わず、ES2015を書くだけにとどめます。

 

パッケージはこんな感じ。

f:id:chuck0523:20160813115653p:plain

シンプルですね。

 

webpackコンフィグ

gist.github.com

 

babelrc

gist.github.com

 

コンパイル

いざ、コンパイルさせます。

 

書いたファイル

こちらが書いたファイル。

gist.github.com

 

変換後のファイル

こちらが変換後のファイル。

 

gist.github.com

 

↑のファイルを少しずつ読んでいきます。

 

全体の構成

構成としては、ざっくりと2つに分かれていそうですね。

前半に関数部分があり、後半は関数に渡す引数部分になっていそうです。

 

ざっくり書くとこんな感じ。

 

gist.github.com

 

即時関数ですね。引数でデータを渡しています。

関数側はデータを受け取る前提で書かれており、即時関数のためbundle.jsを読んだ時点で実行されます。

 

まずは、後半の引数部分を見てみましょう。

gist.github.com

 

引数は要素が1つの配列ですね。前半の関数の引数の名前がmodulesとなっている通り、これは複数のモジュールを格納する配列ですね。

配列の要素は、関数になっています。引数はmoduleとexportsの2つ。

moduleをconsole.log()で見てみると、

{ exports: {}, id: 0, loaded: false } 

とのこと。

exportsは

{}

と空のモジュールですね。

 

なるほど。

 

次に前半の関数部分について見ていきましょう。

gist.github.com

 

ご丁寧に各行にコメントが書かれていますね。

 

構成としては、まず変数定義があり、次に関数定義があり、そのあとに関数に対してプロパティを追加して、関数をreturnしているみたいです。

まずはこちら。変数定義部分。

 

gist.github.com

 

空オブジェクトを定義していますね。モジュールキャッシュとコメントにあります。なるほど。モジュールのキャッシュとは…?

今回はスルーします。

 

次に関数定義部分。ここがbundle.jsの中でも一番行数が多いですね。

 

gist.github.com

 

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__に拡張が行われます。

 

gist.github.com

 

mプロパティには引数で渡されたすべてのモジュールが、cプロパティにはキャッシュされているモジュールが、pプロパティには空文字がセットされています。

pプロパティに関しては、public_pathというものが渡されるようです。今回空文字なのは、おそらくwebpack.config側でそういう指定をしていないからだと思われます。

 

なるほど、各モジュールは、__webpack_require__のプロパティを通じてその他のすべてのモジュールを知ることができるんですね。

 

そして、前半の関数部分は__webpack_require__の実行をreturnして終了します。

gist.github.com

 

この結果返るのは、moduleのexportsプロパティですね。

 

 

なるほど。

webpack変換後のコードが少しだけ理解できました。次回はimport/exportを使った場合の変換結果を見てみたいと思います。