読者です 読者をやめる 読者になる 読者になる
月 の 上

React のソースコードを読んでみよう!

f:id:amagitakayosi:20161207035726p:plain

こんにちは id:amagitakayosi です。
株式会社はてなで主にフロントエンド開発を担当しています。

この記事では React 本体のコードを読んでみます!!

この記事は Reactアドベントカレンダー 2016 の7日目の記事です。
昨日は yutaszk さんで「react-router v4 でFlux アプリケーションをHot Module Replacement する」でした。

目次

はじめに (Kyoto.js の宣伝)

僕は Kyoto.js というコミュニティを運営しています。
Kyoto.js は京都界隈の JavaScript 開発者のためのコミュニティです。
(実際は京都以外のメンバーのほうが多い……)

これまで Kyoto.js では3,4ヶ月おきに勉強会を開催してきましたが、もっと気軽に交流したい!活動を増やしたい!ということで、先月からオンラインコードリーディング会をすることにしました。

コードリーディングは1,2週間おきに1度のペースで、Google hangoutを使って開催しています。
興味のある方は是非 Slack からご参加ください!

準備

github.com

ここでは、 facebook/react のレポジトリの構造について簡単に説明します。

React の処理の概要やディレクトリ構成については、公式のドキュメントである程度解説されています。
英語が苦でない方は読んでおくと良いでしょう。

まずはレポジトリをcloneしてきましょう。
cloneしたら、npm installなどの準備もしておきます。

# まずはクローン
git clone https://github.com/facebook/react

# yarnをつかって依存パッケージをインストール
npm install -g yarn
yarn

# react.js 等をビルド。エラー出るかもしれないけど大丈夫!
npm run build

いくつか注意点!

  • 本記事では 2be0d93c 時点でのコードを参照します。
  • 行番号は (L123) のように表します。

ディレクトリ構造

まずは、レポジトリがどんな構造になってるのか見てみましょう。
ディレクトリ構造は以下のようになっています(面白いとこだけ抜粋)。

react/
├─ src/
│   ├─ addons/
│   ├─ isomorphic/
│   │   ├── React.js
│   │   ├── children/
│   │   ├── classic/
│   │   ├── hooks/
│   │   └── modern/
│   ├─ renderers/
│   │   ├── art/
│   │   ├── dom/
│   │   ├── native/
│   │   ├── noop/
│   │   ├── shared/
│   │   └── testing/
│   └─ testing/
├─ build/
├─ grunt/
├─ gulp/
├─ packages/
├─ Gruntfile.js
├─ gulpfile.js
├─ package.json
└── yarn.lock

monorepo

package.jsonを見ると、このレポジトリ自体のパッケージ名は react-build となっています。
また、 packages/ を見ると、複数のnpmパッケージのpackage.jsonが置かれています。

このように、 React では一つのgitレポジトリで複数のnpmパッケージを管理する構成をとっています。
このような構成のレポジトリは monorepo と呼ばれています。 monorepo は他にも Babel や Angular などの大規模なプロジェクトで採用されています。
また、 monorepo を管理するための lerna というツールも存在します。
monorepo を採用することで、 複数のnpmパッケージで重複するコードを管理しやすくなったり、デバッグが容易になるといったメリットがあるようです。

Haste

src/ 以下のファイルを眺めると、 require('ReactMount') のように、ファイル名べた書きの不思議な require に出くわします。
これは Facebook 内製の Haste というモジュールシステムによるものです。

Haste では、 全てのファイル名をユニークにする という制約を設けています。
ビルド時には、browserify の前段で全てのファイルを lib/ 直下にコピーすることで、 browserify が require() を解決出来るようになっています。

React の src/ はかなり複雑な構造になっていますが、ファイル名がユニークであることで、モジュール名さえわかっていればエディタのfuzzy finderを使って簡単にファイルを開ける、といったメリットがあるようです。

Haste のコードが Facebook 内部でどうやって管理されているのかは分かりませんが、 React においては、この辺で require() しているスクリプトで同様の機能を実現しているようです。
https://github.com/facebook/react/blob/2be0d93c7782eb2dad62efcac9668152da715c25/gulpfile.js#L18-L20

Gulp と Grunt

gulpfile.jsGruntfile.js が両方存在するのに気づきます。
Issueにもある通り、 Grunt から Gulp へ移行途中ということみたいですね。
2013年時点のコード を見てみると、まだ Grunt だけ使っていることがわかります。

Gruntfile の初っ端から gulp を呼ぶための関数を定義しており、涙ぐましい……。
タスクごとに移行していくというのはスマートですね。
大規模なプロジェクトで Grunt から Gulp への移行を検討している方は真似してみても良いかもしれません。

コードリーディング

それでは、早速コードを読んで見ましょう!
今回は React アプリケーションの初期化まわりを探ることにします。
具体的には、 React.render() により React コンポーネントが初期化され、DOM 要素へマウントされるまでの処理を追うことを目標とします。

src/ 以下のファイルは、主に addons isomorphic そして renders の3つに分類されています。
React のコア部分が isomorphic に、 react-domreact-native 等環境に依存したコードが renderes/ に入っています。

react

まずは react パッケージのコア部分から攻めてみましょう。

react パッケージの本体は src/isomorphic/React.js です。
require('react'); した時に export される API が定義されています。

https://github.com/facebook/react/blob/2be0d93c7782eb2dad62efcac9668152da715c25/src/isomorphic/React.js#L54-L92

APIModern Classinc に分類されています。
Modern の方には Component や PureComponent といった ES2015 Class 前提のモジュールがあり、 Classic には createClass や mixin といった懐かしい単語が並んでいますね。

特に重要なのは ReactComponentReactElement でしょうか。

ReactComponent

ソース: src/isomorphic/modern/class/ReactComponent.js

ReactComponent は、我々通常の React ユーザーがコンポーネント作成時に extend するクラスです。
このファイルでは setState forceUpdate が定義されています。
いずれのメソッドでも、 this.state を直接操作したりせず、 this.updater に処理を enqueue するにとどまっています。

下の方で if (__DEV__) としているのは、開発環境で警告を出すためのコードです。
React は非常に丁寧にの警告やエラーを出してくれますが、コードを読んでいると至る所に warning invariant といった警告、エラーの為の処理が挟まれています。
これらの関数は facebook/fbjs 内で定義されています。
fbjs は Facebook 内製のツール群をまとめたレポジトリです。
https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/forks/warning.js

話が逸れました。
さて、 updater はデフォルトでは ReactNoopUpdateQueue が渡されるようですが、これは名前の通り何もしない updater です。
実際に render するときは、適切な updater が渡されるのでしょうか?

git grep updater src でそれらしい箇所を探すと、 ReactCompositeComponent の mountComponent メソッドで updateQueue を渡しているのが見つかりました。

https://github.com/facebook/react/blob/2be0d93c7782eb2dad62efcac9668152da715c25/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#L197

mountComponent はHTML文字列を作って返すメソッドです。
後述しますが、ReactCompositeComponent は ReactComponent に対応する内部表現オブジェクトのクラスであり、ReactComponent の子クラスとかでは ありません
mountComponent でコンポーネント毎の HTML 文字列を生成し、どこかで組み立てているようですが……?

react-dom

今度は react-dom のコードを見てみましょう。
react-dom の本体は src/renderers/dom/ReactDOM.js にあります。
React.js 同様、 require してAPIを公開してるだけですね。

https://github.com/facebook/react/blob/2be0d93c7782eb2dad62efcac9668152da715c25/src/renderers/dom/ReactDOM.js#L31-L41

(unmountComponentAtNode なんて出来たんですね……知らなかった)

ReactDOM.renderReactMount で定義されているようです。

ReactMount

ソース: src/renderers/dom/stack/client/ReactMount.js

ReactMount は Mounting に関する処理を行うモジュールです。
Mounting とは、 React コンポーネントを render し、 container 要素内に DOM ツリーを挿入する処理のことです。

// ReactMount.jsより抜粋
ReactMount.render(
  component,
  document.getElementById('container')
);
<div id="container">       <-- Supplied `container`.
  <div data-reactid=".3">  <-- Rendered reactRoot of React component.
    // ...                              
  </div>
</div>

render メソッドの定義は以下のようになっています。

  /**
   * @param {ReactElement} nextElement Component element to render.
   * @param {DOMElement} container DOM element to render into.
   * @param {?function} callback function triggered on completion
   * @return {ReactComponent} Component instance rendered in `container`.
   */
  render: function(nextElement, container, callback) {
    return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
  },

ReactElement は、type props といったプロパティをもつ Plain なオブジェクトです。
多くの場合は JSX から生成し、 render() で return したりします。
JSX が苦手な方は手動で React.createElement() してるかもしれませんね。
(参考: https://facebook.github.io/react/docs/introducing-jsx.html)

_renderSubtreeIntoContainer は次のような処理をしています。

  • nextElement のラッパーを作成 (L464)
  • _renderNewRootComponent() で取得した ReactComponent のインスタンスを返す (L528)

_renderNewRootComponent の中身はこんな感じ:

  • instantiateReactComponent()nextElementインスタンス化 (L382)
  • ReactUpdates.batchedUpdates() を呼んで、取得した ReactComponent インスタンスを DOM ツリーにマウント (L394)。

「ReactUpdates って何やねん」という感じですが、今は気にしなくて良いです。

batchedUpdates() の引数を辿っていくと、mountComponentIntoNode に行き着きます (L94)。
この関数では、 ReactReconciler.mountComponent()markup を作って _mountImageIntoNode() に渡しています。
_mountImageIntoNode では setInnerHTML したりしてるので (L726)、 markup は HTML 文字列とみていいでしょう。

今度は ReactReconciler を読んで、 markup がどのように作られているか探って行きましょう。

ReactReconciler

ソース: src/renderers/shared/stack/reconciler/ReactReconciler.js

ReactReconciler は一言で言うと「ReactElement、ReactComponent、DOM要素、内部表現をまとめる者」です。

reconcile /rék(ə)nsàɪl/
調和させる, 調整する, 一致させる «with» ; 〈銀行明細など〉と帳尻を合わせる

先ほど「ReactCompositeComponent は ReactComponent に対応する内部表現」と説明しました。
React 内部では、 ReactComponent など render() を持つオブジェクトを public instance , ReactCompositeComponent などの内部表現を internal instance と呼んでいます。

さらに分類すると以下のようになります (ドキュメントを参照)。

internal instance は、対応する public instance や ReactElement への参照を持ちます。
また、 componentDidMount など public instance の持つライフサイクルを呼び出す役目を担っています。

以上を踏まえて ReactReconciler.mountComponent を眺めてみます。
第1引数は internalInstance という名前になっていて分かりやすいですね。 *1
internalInstance.mountComponent() で HTML 文字列を生成し、 refs などの処理をしてから返しています。

となると、今度は internal instance のクラスを読めばいいことがわかります。
composite component, host component の順番に読んでいきましょう。

ReactCompositeComponent

ソース: src/renderers/shared/stack/reconciler/ReactCompositeComponent.js

ファイルを開くといきなり良い図がでてきます。
この図では、public instance のライフサイクルメソッドや render が実行される順序が示されています。
以下転載 (ソース)。

/**
 * ------------------ The Life-Cycle of a Composite Component ------------------
 *
 * - constructor: Initialization of state. The instance is now retained.
 *   - componentWillMount
 *   - render
 *   - [children's constructors]
 *     - [children's componentWillMount and render]
 *     - [children's componentDidMount]
 *     - componentDidMount
 *
 *       Update Phases:
 *       - componentWillReceiveProps (only called if parent updated)
 *       - shouldComponentUpdate
 *         - componentWillUpdate
 *           - render
 *           - [children's constructors or receive props phases]
 *         - componentDidUpdate
 *
 *     - componentWillUnmount
 *     - [children's componentWillUnmount]
 *   - [children destroyed]
 * - (destroyed): The instance is now blank, released by React and ready for GC.
 *
 * -----------------------------------------------------------------------------
 */

public instance の render が再帰的に呼ばれることがわかりますね。

mountComponent の処理の流れは次のようになっています。

  • public instance を生成 (L201-L262)
  • componentWillMount を呼ぶ (L336-L351)
  • markup を生成 (L353-L370)
  • componentDidMount を呼ぶ (L372-L384)

markup の生成は performInitialMount で行なわれます。 こちらは次のような流れ。

  • renderedElement を作る (L516)
  • renderedElement から子供の internal instance を生成 (L521)、
  • ReactReconciler.mountComponent を呼ぶ

renderedElement には public instance の render() が返す ReactElement が入ります (L516, L1184, L1153 の順で呼ばれる)。
この ReactElement は this._currentElement の子要素にあたります。

すなわち、 ReactReconciler.mountComponent()render()再帰的に呼び続ける構造になっています。
React のツリーの末端は必ず DOM 要素などの host component となるので、そこで処理が止まるようです。

ReactDOMComponent

ソース: src/renderers/dom/stack/client/ReactDOMComponent.js

ブラウザ環境での host component 実装は ReactDOMComponent にあります。
ReactHostComponent というのもありますが、これらは継承関係ではありません。 *2

mountComponent を読んでいくと……ありました!
ReactElement の type から document.createElement() したり、 HTML 文字列を生成したりしています! (L569, L604)
前者の場合、 _createInitialChildren を呼ぶことで DOM 要素に子要素を挿入しています。

これでようやく ReactDOM.render() 時の DOM 生成が完了しました!

まとめ

初期化処理をなぞっただけでも、沢山の登場人物がでてきました。
今回でてきた主な登場人物は以下のとおり。

  • ReactElement
  • public instance (ReactComponent)
  • internal instance
    • composite component (ReactCompositeComponent)
    • host component (ReactDOMComponent)
  • reconciler (ReactReconciler)

他にも updater や transaction が出てきましたが今回は割愛します。

ReactDOM.render() した時の処理の流れは、ざっくりいうと以下のとおりです。

  • ReactMount.render() を呼び
  • ReactMount._renderSubtreeIntoContainer() で一番上の ReactComponent をインスタンス化し
  • ReactReconciler.mountComponent()render()再帰的に呼び
  • ReactDOMComponent.prototype.mountComponent で DOM 要素を生成する

おわりに

後半箇条書きばっかりになってしまった 😇
React ユーザーの方もそうでない方も、この記事で React の実装に興味を持っていただけたら幸いです。

よろしければ Kyoto.js Slack も覗いてみてください!

それでは!!


この記事は Reactアドベントカレンダー 2016 の7日目の記事です。
明日は chimame さんで「Webpack依存のReactコンポーネントをテストする」です。

*1:コメントでは ReactComponent となっていますが、多分間違いだと思います

*2:ドキュメントで解説されています https://facebook.github.io/react/contributing/codebase-overview.html#dynamic-injection

กระต่ายหมายจันทร์

タイで4日間過ごした。

知らない文字に囲まれて、本当に着くのかと不安になるバスに乗ったりしてた。 川と海と高層ビルの隙間に、水色と緑とピンクと金と、土色と、あと無数に吊るされた黒い電線の中を、フワフワさまようような旅だった。

経緯

旅行が決まったのは3ヶ月前。2,3日のうちにエクスペディアで航空券とホテルを予約して、あとへ引けない状況になった。 その後、GitHubのprivateレポジトリでissueを立てて、一日ごとのスケジュールとか、移動手段を検討していった。

f:id:amagitakayosi:20161201005800p:plain

SIMカードとかGrabとかの情報を恋人がどんどん収集してくれてメチャクチャ助かった……!

お役立ち情報

ホテル

Amari Watergate Bangkok というホテルに泊まった。

アマリウォーターゲートバンコク - プラトゥーナム地区にあるバンコクのホテル

エクスペディアで施設と場所を見て決めたんだけど、あとで調べると日本語サイトがあったり、日本人スタッフが居たりした。 日本人スタッフの人は調べ物やお店の予約などを手伝ってくれて凄く助かった。 部屋も綺麗で過ごしやすく、はじめてタイに行く人にはオススメできる。

SIMカード

SIMカードはtrueのものを利用。 ドンムアン空港のカウンターは6:00開店とのことだったけど、5:40ごろには開いていた。 店員のお姉さんはずっと髪を編み込みながら話してたが、無事LTEのSIMを入手。 4日間で7GB、意外と充分だった。

Grab

ドンムアン空港からバンコクまでは Grab を利用。 東南アジアで展開してるUberみたいなやつ。

https://www.grab.com/

都市部の移動は大体これか、メータータクシーを利用した。

タクシー

「METER TAXI」って書いてあるけど実際メーター使わないタクシーもあるので注意。 乗る前に確認しよう。

ぼったくり

タイ旅行で「気をつけろ」と言われるのがぼったくり。

今回はタクシー、バス、船で移動したけど、意外とボラれることは無かった。 タクシーでちょっと高めに取られることはあったけど、日本円で数十円程度だし、まあいいか、という感じ。 一番交渉が大変そうなトゥクトゥク、ソンテオには乗らなかったので、その辺は分からない。

地球の歩き方やブログでみた「ワットポーはは今日やってないよ!」おじさんは存在した。 ワットポーへ向かう王宮近くの道で遭遇。 その日は前国王の弔いイベントの日で、王宮を見るのは諦めていたので、王宮への道を塞がれても困りはしなかったが、強引に宝石店に連れて行かれることもなく、おじさんの目的は不明。

食事

うまい!!!!!!!!!!!! 屋台のパッタイが美味しすぎた

  • 英語通じなくてもジェスチャーと笑顔で頑張ろう
    • 都会のお店だと英語が通じる
  • 屋台が沢山あるので、ちょっとずつつまみ食いして歩くのもよさそう
  • 食費は店によって全然違うけど、だいたい日本の 1/3 〜 1/2程度
    • 瓶ビールが 80バーツ (240円) くらい
    • 屋台だとパッタイ一人前 40バーツ (120円)
    • アユタヤで名物のエビ食べたら 1600バーツ (4800円) だった

コンビニ

  • セブンイレブンがどこにでもある
    • まれにファミマもある
  • プリッツ ラーブ味」とかある
    • 空港のお土産屋さんとかにはないので、早めに買っておこう
  • レッドブル系のスタミナドリンク、なぜか炭酸ない

以下、写真


電車もバスもタクシーも乗り方がわからないので、拙い英語で話しかけまくったり、とかく行動力になって強くなった気がしたんだけど、帰ってきたらどこか遠い国の出来事なんだよなあ。

帰国から1週間たって、良かった記憶だけが残っている。

エンジニア立ち居振舞い「自分の担当範囲を意識する」

お題「エンジニア立ち居振舞い」

僕は今のチームでフロントエンド担当ということになっている。 一応肩書は「アプリケーションエンジニア」だしサーバー側のコードもガンガン書くけど、他のエンジニアとの立ち位置を考えると自然と担当範囲が決まってくる感じだ。

担当範囲の決め方

うちの会社ではWebサービス開発を行うエンジニアを「アプリケーションエンジニア」として一律に扱っているが、その実メンバー毎になんとなく担当範囲が存在する。 担当範囲は、個人的な嗜好、経緯、あとはチーム内での分担で決まる。

僕の場合は、フロントが好きということもあったし、チームに入ってすぐフロントのタスクをゴリゴリ進めた結果、すんなり現在のポジションに落ち着いた。 チーム内で一番JS書ける必要はなくて、チーム内での比較優位で決まることもある。

今のチームでもう2年以上も働いていて、その割に知らない仕様とか沢山でてくるけど、それらを全部把握しようとは考えてはいない。 わからない事があれば知ってる人に聞けばよい、と考えている。

メリットとデメリット

たとえば、タスクの内容によって反応すべきかどうかフィルタリングできる。 Slackでは沢山のチャンネルにjoinしてるけど、ひと目見て「これは○○さんの分野だな」と思ったらスルーするようにしてる。 GitHubのissueも同様。 全部見るという人もいるけど、僕は仕事に集中してる間Slackに気づけないタイプなので、気にせず流すようにしてる。

あと、担当範囲はどこかでアピールしておいて、他のメンバーにも共有しておく。 そうすると自然と「画面でバグでたら @amagitakayosi に報告しよう」とか「npmの話題だから @amagitakayosi に聞こう」となってくれて、とても助かる。

担当範囲を意識することで自分の可能性が狭まるかというと、そうでもないはず。 名刺に書けるような自分のスキルを追加するぞ、くらいの気持ちになれる。 社内で新プロジェクト始まる時に「このJSどう思う」みたいに聞いてもらえたりして嬉しい。

ただ、自分の好きじゃない分野の担当になったら、あんまり我慢せずとっとと切り上げたほうが効率良いと思う。

社内ブランディングとチーム間コミュニケーション

担当範囲がはっきりしたエンジニアが集まると、チーム間でのコミュニケーションがしやすいというメリットもある。 うちの会社では、インフラ系の人やスマホアプリエンジニアは担当範囲がはっきりしていて、ミーティングを開いたり、ある種の独立自治体制を築いている。 フロントエンドはまだまだだけど、フロントエンドランチ みたいな活動を続けていって、いずれいい感じになりたいなと妄想している。

2016-10-09 冷蔵庫、WordPress

冷蔵庫が届いた

ドアチャイムの音で目が覚めた。 10時過ぎに冷蔵庫が到着。 冷蔵庫の中身や、上に載せた電子レンジ、炊飯器を急いで退ける。

作業員は二人組。異常に手早くて、古い冷蔵庫の回収から新しい冷蔵庫の設置まで3分ほどで完了してしまった。 古い冷蔵庫は家電リサイクル法で回収してくれる(3000円くらい)。

f:id:amagitakayosi:20161015144308j:plain

新しい冷蔵庫はこれまでに比べるとずっと広いので、中が寂しく見える。 しばらくは食器とか入れておこうかなあ。

マルニカフェ

tabelog.com

家のすぐ側にあるんだけど、なかなか機会がなくて入れなかった。 日替わりランチを注文。結構ボリュームあって良い。 スイーツと称してたい焼きを売ってるので、冬にふらっと寄って食べてみたい。

WordBench 京都 & ng-kyoto 10月勉強会

wb-kyoto.connpass.com

13:00 からはこちらの勉強会に参加。

弊社ではフロントエンドランチと称して、昼休みに技術相談をしたり、技術ニュースやイベント情報をチェックしたりしている。 そこで dots の JavaScript タグを眺めていたら ng-kyoto の名前を見つけた。

最近たまたま WordPress 開発の話を聞く機会があり、自分の守備範囲とは異なる「WordPress界隈」について興味をもっていたところだった。

京都駅から徒歩6分!コワーキングスペースcoto(コト) - cotoworking ページ!

会場は京都駅近くのコワーキングスペース coto。 看板などはなく、建物の外から coto の場所はわからないので少し困った。 入り口にフラダンス教室のポスターと獺祭の瓶が置いてある建物だ。

会場にいたのは20名程度? 小さな会場だが、 WiFi も電源もあるということで、駅前でちょっと作業するのには良さそう。 ただ、イベント中に「一斉に npm install したら回線詰まるので気をつけて!」とアナウンスされるなど、良識が問われる感じ。

発表内容については Twitterの#wbkyotoタグ を参照。

個人的に印象に残ったのは男木島図書館の話。先月男木島に行ってきたばかりだ。 なんと男木島図書館を作ったのは WordPress コミュニティの人だったらしい。 「男木島 wordpress」でググると、他にも中の人がiPhoneアプリをデザインした話などが出て来る。 前回の旅行では残念ながら定休日で行けなかったけど、次に行く時はそういう話も聞けたら楽しいかも。

WiMAX2端末が届いた

4年使ってるWiMAX端末が壊れてきたので、WiMAX2を新規契約した。 これまでに比べると速い気はする。 試しにKUNG FURY見てみたけどストレスなく視聴できた。やったー

www.youtube.com

ただ、動画みると3GB/3日制限に一瞬で引っかかりそうなのは気になる。

スーツを受け取った

四条の P.S.FA で注文していたスーツを受け取った。 どんな場面でも着られるやつがよい、ということで無難な黒いスーツにした。 店員に勧められるままに買ったところトータルで5万弱飛んだ……。 スーツなんて年に2,3回しか来ないので、元を取るために10年以上使いつづけたい。

WordPress入門した

用事が済み、オフィスに寄って WordPress 入門してみた。 WordPressWordPress.org で試したり、大昔にレンサバで動かした程度の知識しかない。

とりあえず手元で動かして記事を書いたり管理したりしてみる。 modern wordpress でググってヒットした諸々を試す。

手元で動かすなら WP-CLI で充分そう。 VVV は Vagrant 上で動かす環境。なんか複雑なことしてそうだけど何が嬉しいんだろう(README.md長い……)。

手元で動かして本体のコードちょっと読んで飽きた。 また機運高まったらプラグインとか書いてみると楽しいのかもしれない。

2016-10-08: 京都芸術センター, ヨドバシ

京都芸術センター / 前田珈琲

読書したりちょっと作業したりするために図書館へ行こうと思っていたのだけど、どうやら京都の図書館ではPCを開いたり勉強するのはNGらしい。 大学時代に図書館で好き勝手していたので感覚がマヒしていたのかな。 京都で自習できる場所を探すと、大学生向けのまとめサイトがいくつかヒットする。 その内のひとつに京都芸術センターがあった。

京都芸術センターに来るのは、インターンで京都に初めて来たとき以来だ。 ここは廃校になった小学校を改装した施設で、現在ではカフェ、小さな図書館(?)、ギャラリーなどとして利用されている。 京都にはこのタイプの施設が特別多い気がする。

この日は同時に3つくらいの展示が行われていたけど、今回の目的は読書等なのでスルー。 今週の土曜にでも行こうかな。

https://tabelog.com/kyoto/A2601/A260201/26001901/tabelog.com

13時頃、前田珈琲で昼食。 ちょっと値段高めだけど、誰かとおちついて会話するのには良さそうだ。 店内は若い女性2,3人組や、中高年のグループなどで賑わっている。 四条烏丸から近い割には落ち着いている方かもしれない。 元小学校にしては、やけに天井が高い。調理室はタイル張りだったりして、教室じゃないことはわかるんだけど、何の部屋だったんだろう?

赤味噌ハヤシライスを注文。赤味噌の味はわからないが、卵が暖かくておいしい。 アイスコーヒーはだいぶ味が濃い方だった。

17時まで談話室で読書、作業。 高校生カップルが受験勉強していたり、ノマドっぽい人が作業していて良かった。

フィクションで親しんだ古風な雰囲気で、廊下でぼうっとしていると、知らないことを思い出しそうになる。

ヨドバシで冷蔵庫購入

冷蔵庫が壊れて困っていた。 霜取り機能が壊れて排熱できなくなってしまったらしい。 一度手で溜まった氷を掻き出してみたら2週間ほど正常に動作したんだけどやっぱりダメで、中の野菜やら何やらを腐らせてしまった。 来年あたり引っ越そうと思っているので、せっかくだから新しい冷蔵庫を買うことにした。

GitHub の issue に値段や懸念点を書いていく。 白くて、安くて、270リットル位で、という条件で探したところ、3つほど候補が挙がった。

f:id:amagitakayosi:20161015135200p:plain

ヨドバシの店頭で実際にモノを見てみた。 結果、今回はSHARPの冷蔵庫を買うことにした。

見た目とサイズ感が丁度いい。 プラズマクラスターは不要なのでもうちょっと安くしてほしい。

テーブルで書類に記入している間、やたら気さくなお兄さんが水を差し出してきて、1分くらいで早口に家庭用ウォーターサーバーの営業をされた。 ヨドバシの従業員はいろんな仕事するなあ。

Yarn速かった

Yarn 速かった

Yarn とは

https://code.facebook.com/posts/1840075619545360

Facebook, Google, Tilde, Exponentの人たちが作ったnpmクライアント。 npm install の代わりに使える。

http://qiita.com/mizchi/items/1002fde0de10e7c54fb2

詳しくはこの辺

実行時間測ってみた

試しに業務レポジトリで npm installyarn を比べてみた

  • dependencies + devDependencies : 70 個
  • npm-shrinkwrap.json : 22298 行
  • 元の node_modules : 22 KB

npm install

303 sec

$ rm -rf node_modules
$ npm cache clean
$ time npm install
(中略)
npm install  268.92s user 71.13s system 93% cpu 6:02.97 total

キャッシュなしで yarn

103 sec

 $ rm -rf ~/.yarn-cache
 $ rm -rf ~/.yarn
 $ rm -rf node_modules
 $ time yarn
yarn install v0.15.1
error npm-shrinkwrap.json found. This will not be updated or respected. See [TODO] for more information.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
warning benchmark@1.0.0: The engine "rhino" appears to be invalid.
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
✨  Done in 102.55s.
yarn  56.30s user 27.98s system 81% cpu 1:42.99 total

キャッシュありで yarn

37 sec

$ rm -rf node_modules
$ time yarn
yarn install v0.15.1
error npm-shrinkwrap.json found. This will not be updated or respected. See [TODO] for more information.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
warning benchmark@1.0.0: The engine "rhino" appears to be invalid.
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
✨  Done in 36.91s.
yarn  21.12s user 13.32s system 92% cpu 37.299 total

はえ〜