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

コンパイラかく語りき

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

【JSer向け】ローカルで生のRethinkDBを触る

RethinkDB

RethinkDBについて。

 

続きを読む

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

WebPack

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

前回↓ 

chuckwebtips.hatenablog.com

 

前回は、webpackがexportやmodule.exportsをどのように変換するかについて見てみました。

今回は、モジュールのimportやrequireがどのように処理されるかを見てみます。

 

まず、このようなファイルを作りました。(./modules/greet.js)

gist.github.com

 

ES6 import

これをindex.jsからimportして一部を実行してみます。

 

gist.github.com

 

すると、変換ファイルがこのとおり。

gist.github.com

 

モジュール定義が2つになりました!

2つ目のモジュールについては、前回見たとおりですね。1つ目のモジュールについて詳しく見ていきます。

 

gist.github.com

 

まず、関数の引数が増えていますね。第三引数として__webpack_require__が渡されています。どうやらimportを行うモジュールには、これが渡されるようですね。

 

そして、さっそく__webpack_require__(1)という記述が。

これはgreet.jsモジュールをロードしてますね。ロードした結果、module.exportsが返るので、_greet変数にはgreet.jsで定義したモジュールへの全参照が含まれます。

 

gist.github.com

 

その次はこんな感じ。

 

gist.github.com

 

_interopRequireDefaultという関数が使われていますね。引数にオブジェクトを取り、オブジェクトを返します。

オブジェクトが存在しており、__esModuleキーがtrueかどうかで、オブジェクトそのものを返すか、{ default: obj }を返すかを判定していますね。なるほど。

 

最後にimportしたモジュールの実行を行います。

 

gist.github.com

 

むむ、、見慣れない記法ですね。

_greet2.default()じゃあダメなんですかね…?謎です。変換前のJavaScriptが複雑化してきたら意味を持つんですかね。ひとまず今はスルーします。

 

CommonJS require

次はrequireを使った場合について見てみます。

 

まず、greet.jsをCommonJS式に書き換えます。

 

gist.github.com

 

次にindex.jsでの読み込みをrequireに変更。

 

gist.github.com

 

このように変換されます。

 

gist.github.com

 

なるほど!わかりやすいですね。

__webpack_require__が2度呼ばれていますね。ここでモジュールキャッシュが効いてくるんですね。(第一回を参照)

 

 

ES6 import * as hoge

再びES6式に戻って、import * as ~ 形式を試してみます。

greet.jsをES6のexportに書き戻しておきます。

 

gist.github.com

 

import側はこんな感じ。

 

gist.github.com

 

変換後のbundle.jsのうち、index.jsの部分はこんな感じ。

 

gist.github.com

 

むむ。

export defaultの時は、_interopRequireDefaultが呼ばれましたが、今回は、_interopRequireWildcardが呼ばれていますね。

 

ここはBabelの機能のようですね。importの種類によって、異なる関数を生成します。

export hoge & import hoge from 'hoge' -> __webpack_require__のみ

 

export default hoge & import hoge from 'hoge' -> __webpack_require__ + _interopRequireDefault

 

import * as hoge from 'hoge' -> __webpack_require__ + _interopRequireWildcard

 

ざっくりと。

 

_interopRequireWildcard関数を抜き出してみました。

 

gist.github.com

 

まずは、受け取ったオブジェクトのesModuleプロパティがtrueかどうか判定していますね。

 trueではない場合には、オブジェクトをそのままreturnせずに新しいオブジェクトを生成してreturnしています。

 

 

というわけで、今回はimport / requireがどのように変換されるのか挙動について見てみました。これで、webpackがimportとexportをどのように扱うかを知ることができました。

 

次回は、複数バンドルについて見ていきます。

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

WebPack

引き続き、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)

JavaScript WebPack

ずっと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の画像の回転/拡縮/反転

Swift

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

 

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

 

できたもの

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

f:id:chuck0523:20160714125350p:plain

 

書いたコード

gist.github.com

 

学び

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

001 UIKit - 013 UITabBarControllerでタブの表示

Swift

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の表示

Swift

iOS開発のお勉強。

教材:012 UINavigationControllerの表示 - Swift Docs

 

UINavigationControllerについて。

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

 

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

 

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

qiita.com

 

書いたコード

gist.github.com

 

gist.github.com

 

gist.github.com