読者です 読者をやめる 読者になる 読者になる

コンパイラかく語りき

文系新卒プログラマーのメモ

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

引き続き、webpackの仕組みについて見ていきます。

 

chuckwebtips.hatenablog.com

 

前回書いたコードはこちらでした。

 

gist.github.com

 

本当に単純な定数定義ですね。

実際の開発では、JavaScriptはモジュール単位で書くのが基本だと思います。なので、まずはモジュール化を行います。

 

ES6 export

 

gist.github.com

 

まずは、CommonJS形式ではなくES6のexport を使います。また、実行されたかどうかがわかりやすいように、処理分にconsole.logを書きました。

変換後のbundle.jsがこちら。

 

gist.github.com

 

前半部分は前回解説したものと変わっていなさそうです。

後半のモジュール部分が変わりましたね。

 

gist.github.com

 

Object.definePropertyを使用している部分と、関数定義部分に分けることができそうです。

 

まずはObject.defineProperty部分について。

 

gist.github.com

 

exportsオブジェクトに対して、__esModule__ というキー名で、true値をセットしています。

definePropertyを使うことによって、暗示的にプロパティの属性をfalseにすることができます。

プロパティの属性については、以下の記事がとてもわかりやすかったです。

十一章第四回 Objectとプロパティ — JavaScript初級者から中級者になろう — uhyohyo.net

 

つまり、exportsに__esModule__プロパティを追加したけど、それは上書き不可能だし、列挙されないし、そういった設定を変更することは不可能、ということです。

なので、console.log(exports)で表示しても、__esModule__プロパティは見えません。

 

次に、関数定義部分について。

gist.github.com

 

var helloとfunction(){...} の間にexports.helloが挟まりましたね。

exportsのプロパティとしてhello関数を定義しているんですね。

 

exportsプロパティはmoduleオブジェクトに含まれており、moduleオブジェクトはinstalledModulesというオブジェクトに含まれているので、すべてのexportはinstalledModulesから辿れることになりますね。

 

 

CommonJS export

では、これをexportではなくCommonJS形式のmodule.exportsに変えてみます。

 

gist.github.com

 

変換後のモジュール定義部分がこちら。

 

gist.github.com

 

なるほど!w

引数で受け取っているmoduleのexportsプロパティに追加を行っていますね。ただし、module.exportsはもう1つの引数であるexportsと同じものです。

そして、CommonJS式の場合は特に__esModule__: trueのような目印はつけないんですね。

 

複数モジュールの定義

なんとなく予想はつきますが、複数のモジュールを定義した場合も見てみます。

gist.github.com

 

変換後はこんな感じ。

gist.github.com

 

exportsプロパティとして複数定義される感じですね。

 

export default 

では、ES6式のexport defaultによるモジュール化はどうなるでしょう。

 

一番最後にdefault exportを追記しました。

 

gist.github.com

 

変換後はこのとおり。

 

gist.github.com

 

なるほどw

exports.defaultのプロパティとして追加されました。import時にどのように処理されるのか、楽しみです。

 

 

 

以上、JavaScriptコードのモジュール化をwebpackがどのように処理するか見てみました。

次回は定義したモジュールを外部からimport/requireしてみます。

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を使った場合の変換結果を見てみたいと思います。

001 UIKit - 014 UIImageViewの画像の回転/拡縮/反転

iOS開発のお勉強。今回は画像周りについて。

 

教材:014 UIImageViewの画像の回転/拡縮/反転 - Swift Docs

 

できたもの

画像がいろんな形式で表示されています。

f:id:chuck0523:20160714125350p:plain

 

書いたコード

gist.github.com

 

学び

UIViewImageのtransformに対して、CGAffineTransformMakeRotationやCGAffineTransformScaleで生成した値を入れるところがポイントっぽですね。

001 UIKit - 013 UITabBarControllerでタブの表示

iOSのお勉強。今日はタブです。

 

教材:013 UITabBarControllerでタブの表示 - Swift Docs

 

できたもの

f:id:chuck0523:20160714123428p:plain

画面下の方にタブが2つあります。デフォルトでは、Featuredが選択されている状態です。

ここでHistroyをタップすると、

f:id:chuck0523:20160714123445p:plain

画面が切り替わります。

 

書いたコード

AppDelegateでタブの生成して、windowに貼り付けてますね。

gist.github.com

 

gist.github.com

 

gist.github.com

あとは、2つの画面のビューコントローラを作るだけです。

 

学び

前回に引き続き、画面遷移のある実装を作りました。

 

chuckwebtips.hatenablog.com

 

なるほどなるほど。

AppDelegateで遷移関係を制御して、個々のビューコントローラで各ページの記述を行う感じですね。

001 UIKit - 012 UINavigationControllerの表示

iOS開発のお勉強。

教材:012 UINavigationControllerの表示 - Swift Docs

 

UINavigationControllerについて。

今回、はじめて、複数ページを実装しました。こんなに簡単にできるとは。。

 

UINavigationは複数ページのナビゲーションであって、複数のページは複数のViewControllerファイルを作ればいいんですね。

 

この記事も参考になりました。

qiita.com

 

書いたコード

gist.github.com

 

gist.github.com

 

gist.github.com

Relayアプリケーションに、routerを導入する

RelayでSPAを作っています。前回までで、ひとまず形には為りました。

 

chuckwebtips.hatenablog.com

 

ただ、現在の状態だと1つのページしかありません。

 

f:id:chuck0523:20160704040429p:plain

(求人一覧が表示されている状態)

 

求人をクリックしたら、求人の詳細画面にうつる。みたいな処理をReact-routerを利用して追加します。

 

できるかなあ。。。

 

 

参考にしたプロジェクト

react-routerおよび、react-router-relayの使い方については以下のプロジェクトを参考にしました。

github.com

 

パッケージインストール

必要なパッケージをインストールします。

$ npm install --save react-router react-router-relay relay-local-schema history@2.1.2

Relayアプリケーションでルーターを使うには、react-routerだけではなくreact-router-relayも必要みたいです。

さらに、historyというパッケージもインストールします。これは、ブラウザの履歴等をカンタンに扱うことができるパッケージのようです。

 

github.com

 

便利ですね。

ちなみに、コンソールからwindow.historyと入力すると、実際にhistoryを見ることができます。

 

f:id:chuck0523:20160704041155p:plain

 

バージョンを指定するのは、他のパッケージとの依存性のためです。

 

データ追加

新ページのためのデータを追加します。

まずは、擬似的なデータベース定義を行っているdatabase.jsに対して追加をします。

 

gist.github.com

(修正箇所のみ抜粋)

detail というカラム名で求人の詳細情報を追加しました。

 

次に、スキーマも修正します。

gist.github.com

 

React側から取得できるように、スキーマにdetailというフィールドを追加しました。

 

データベースとスキーマを変更したので、更新コマンドを打ちます。

 

$ npm run update-schema

 

 

新ページ作成

新しいページを作成します。

gist.github.com

 

まだ作りかけですが、↑のような感じになるかなと思います。

ルーティングで選択されたJobのIDを渡してやって、それをもとに表示すれば良さそうです。

 

ホントは、Array.prototype.filterを使わずに、GraphQLでクエリを発行する際に単一のJobを取得すれば良いのですが、まだGraphQLに慣れておらず…。あとでやります。

 

ルーティングの作成

来ました。今回のメイン。楽しいポイントですね。

 

(と思いきや、ここで3時間ハマりました。)

 

まず、./src/routes.js を新規作成しました。

gist.github.com

 

ここで振り分けを行っています。

新しく生成したルーティングファイルをアプリケーションのルートで読み込みます。

 

ルートのファイルは以下のようになりました。

gist.github.com

ReactDOM.render内にて、<Router />のroutesの引数として、ルーティングファイルを渡しています。

 

コンポーネントの新規作成

ここで、新しいコンポーネントをいくつか作成しました。

f:id:chuck0523:20160704071829p:plain

 

まず、共通パーツがAppとFooter、およびFooterから呼ばれるAboutとHelpです。

 

以下、個別のページとして設定した個別のコンポーネントについて。

まず、ルートのパスにアクセスするとHomeコンポーネントが呼ばれます。これは、ルーティングでIndexRouteとして設定しました。

 

f:id:chuck0523:20160704072312p:plain

(Home画面)

 

ヘッダのWelcome!と、フッタのリンク集が共通パーツですね。フッタのTopがHome画面へのリンクで、AboutとHelpはそれぞれ、以下のページに遷移します。

 

f:id:chuck0523:20160704072433p:plain

About画面。

 

f:id:chuck0523:20160704072443p:plain

Help画面。

 

これらはそれぞれ、react-routerのLinkというコンポーネントを用いて遷移をしています。

 

そして、メインですが、Home画面の'Look at JobBoard!'というリンクをクリックすると、

 

f:id:chuck0523:20160704080800p:plain

求人一覧画面に遷移します。

やりました!ページ遷移ができました!

 

さらに、see detailを押してみましょう。試しに、ux-designerのsee detailを押すと、、、

 

f:id:chuck0523:20160704081239p:plain

ux-designerの求人個別画面に飛びました!

 

無事、Relayアプリケーションにルーティングを導入することができました。

 

コードはこちらにあります。

github.com

001 UIKit - 011 UIPickerViewで値の選択

iOS開発のお勉強。今日はUIPickerViewについて。

 

教材→011 UIPickerViewで値の選択 - Swift Docs

 

こんなやつができました。↓

f:id:chuck0523:20160703164119p:plain

 

学び

いつぞやのUITableと同じく、データソースメソッド、デリゲートメソッドが必要だった。

 

書いたコード

gist.github.com