CSSのcanvasとviewportとposition:fixedとpinch zoom

  • specを元にした概念の整理
  • 間違いあったら教えて欲しい

CSS 2.1におけるviewport

CSS 2.1におけるviewportを説明するにあたり、以下のterminologyが必要となる

canvas

For instance, user agents rendering to a screen generally impose a minimum width and choose an initial width based on the dimensions of the viewport.

viewport

User agents for continuous media generally offer users a viewport (a window or other viewing area on the screen) through which users consult a document.

When the viewport is smaller than the area of the canvas on which the document is rendered, the user agent should offer a scrolling mechanism.

initial containing block

The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has the dimensions of the viewport and is anchored at the canvas origin; it is the page area for paged media. The 'direction' property of the initial containing block is the same as for the root element.

  • ブラウザ(=screen mediaのUA)のスクロールバーを伴った描画領域 = viewport
  • レイアウトはcanvasに描画される
  • initial containing block(デフォルトのcanvas領域)のサイズはviewportの幅と高さに依存する.

viewportが1000pxの場合にhtml要素のwidthを100000pxにしたらどうなるのか?については、overflowプロパティによって定義されていて、

UAs must apply the 'overflow' property set on the root element to the viewport. When the root element is an HTML "HTML" element or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child, user agents must instead apply the 'overflow' property from the first such child element to the viewport, if the value on the root element is 'visible'. The 'visible' value when used for the viewport must be interpreted as 'auto'. The element from which the value is propagated must have a used value for 'overflow' of 'visible'.

のように,

  • root要素のoverflowプロパティがviewportに対しても適用される
  • viewportのoverflowプロパティがvisibleである場合、autoとして解釈されるので、スクロール機能の有無はUA依存となる

position:fixedの挙動

In the case of handheld, projection, screen, tty, and tv media types, the box is fixed with respect to the viewport and does not move when scrolled.

For other elements, if the element's position is 'relative' or 'static', the containing block is formed by the content edge of the nearest ancestor box that is a block container or which establishes a formatting context.

要は、対象のボックスがviewportに対しての固定座標に配置されるということ.

CSS Device Adoptationでの拡張

CSS Device Adaptationでは、meta[name="viewport"]でのviewportサイズの指定を可能にしたiOS Safariなどの実装を追認を行う形で仕様が策定されており、ここでは、initial viewportとacutual viewportの二種類が定義されることになった。

initial viewport

This refers to the viewport before any UA or author styles have overridden the viewport given by the window or viewing area of the UA. Note that the initial viewport size will change with the size of the window or viewing area.

actual viewport

This is the viewport you get after the cascaded viewport descriptors, and the following constraining procedure have been applied.

たとえば、meta[name="viewport"][content="width=device-width"]を指定した場合は以下のようになる

  1. initial viewportがブラウザのウィンドウサイズや描画領域に基づき決定される
  2. UAスタイルおよびユーザースタイルによるmeta[name="viewport"]@viewportの計算が行われる

    1. meta要素によるwidth=device-width@viewport { width: 100vw; }として取り扱われる
    2. ここでの100vw1で計算されたものが取り扱われる
    3. width: 100vw;max-widthmin-widthマップされる
    4. 3でマップされた結果を元に、actual viewportの幅を計算する
  3. 計算の結果、deviceの幅=ウィンドウの幅を自身の幅としたactual viewportが算出される

  4. actual viewportを元にinitial containg blockを再度計算する

ここでもUAスタイルがあるために、仮にviewportの指定が行われていない場合は、従来のサイトとの互換性のために、モバイルブラウザでは自動的にviewportの横幅が1000px前後に設定されたりするようになる。

デスクトップブラウザについては@viewportUAスタイルを持たないと解釈すればいいのだろうが、dbaronによるissue表記があるので、完全に定義が確定しているわけではないのだろう。

では、メディアクエリはどこで計算されるのか? それは以下のように定義されている:

  1. Cascade all @viewport rules using the initial viewport size for values and evaluations which rely on viewport size
  2. Compute the actual viewport from the cascaded viewport descriptors
  3. Cascade all other rules using the actual viewport size

つまり、viewportの計算が全て終わらないとmedia queryをはじめとした計算は起こらない

zoom

ズームについては、まず、CSSOM Viewにて二種類のズームがあることが述べられている:

There are two kinds of zoom, page zoom which affects the size of the initial viewport, and pinch zoom which acts like a magnifying glass and does not affect the initial viewport or actual viewport.

この詳細についてはCSSOM View側では「CSS Device Adaptationを参照」とあるのだが、CSS Device Adaptationには明確なterminologyが設定されているわけではなく、断片的に文章中に記述されているだけである。

When the actual viewport cannot fit inside the window or viewing area, either because the actual viewport is larger than the initial viewport or the zoom factor causes only parts of the actual viewport to be visible, the UA should offer a scrolling or panning mechanism.

This is a magnifying glass type of zoom. Interactively changing the zoom factor from the initial zoom factor does not affect the size of the initial or the actual viewport.

position:fixedとpinch zoomを組み合わせるとどうなるのか

今までに述べた内容をまとめると、

  • viewportはブラウザ(=screen mediaのUA)のスクロールバーを伴った描画領域
  • position: fixedは、対象のボックスをviewportに対する固定座標に配置する
  • pinch zoomはactual viewportに影響を与えない

となるのだが、pinch zoomをした場合に、Android BrowserやiOS Safariでは常にUAの描画領域の枠の固定位置にposition: fixedが配置されるのは、仕様上正確なのかどうか分かり難い…… pinch zoomではviewportに影響を与えないとあるが、zoomをしている以上は、UAとして表示している領域としてのviewportの横幅は相対比で小さくなっているはず。ちなみにW3C Bugzillaでは特に何も見つからず……

pinch zoomとpostion: fixedへの各UAの対応

IE11 Mobileや最近のChromiumでは、この問題に対して、「pinch zoomを行っても、position: fixedを常にUAとして表示している領域に配置しないようにした」(私の知りうる限り、Firefox修正を検討中である)

どのような変更を行ったのかはChromiumの変更を開設したスライドがわかりやすい。

このスライドでは、viewportとされるものは正確には、

  • visual viewport: UAとして表示している領域という意味でのviewport
  • layout viewport: レイアウト計算に用いられるviewport

の二種類が存在するとしている(正確には、この二種類が存在しているとすることで、問題を解決することに成功した)。

pinch zoomの存在しなかった時代(CSS 2.1での定義)では両者は統一して扱われるものであったのだが、モバイルブラウザ(およびUIとしてのpinch zoom)の登場により、分割して扱う必要が出てきた。しかし、現行のCSS Device Adaptationではそこにまで踏み込んだ定義がされていないために話がややこしくなっている。いや、この一件に限らず、CSS Device Adaptationは出来がいいspecとは思えないですけどね……

ちなみにWindow.scrollX/Ywindow.innerWidth/Heightなどのviewportのスクロールに絡む座標は、Chrome Beta for Android 40.0.2214.69で確認したところ、visual viewportを基準に算出されるようになる。まあそうするしかないですよねcompat的にも。なので、

The innerWidth attribute must return the viewport width including the size of a rendered scroll bar (if any), or zero if there is no viewport.

The scrollX attribute attribute must return the x-coordinate, relative to the initial containing block origin, of the left of the viewport, or zero if there is no viewport.

以上の定義中にある"viewport"は, visual viewport。ややこしい。

まとめ

図にするとこんな感じ。仕様+実装を元に、viewportというものは、このような概念であると解釈が出来る。

尚、visual viewportとlayout viewportという単語は、Chromiumのスライドのやつが都合がいいから使ってるだけね。

   +---------------------+----------+
   |                     |          |
   |  *--------*         |          |
   |  |        |         |          |
   |  |  visual viewport |          |
   |  |        |         |          |
   |  |        |         |          |
   |  |        |         |          |
   |  *--------*         |          |
   |                     |          |
   |                     |          |
   |   layout viewport   |          |
   |                     |          |
   |                     |          |
   +---------------------+          |
   |                                |
   |       canvas                   |
   |                                |
   +--------------------------------+

  • directionがltrの場合, 左上からactual viewportが始まる
  • layout viewport = actual viewport
  • layout viewportはinitial contaning blockの大きさを決める
  • visual viewportはpinch zoomなどにより虫眼鏡のようにサイズが 可変したり、スクロールしたりする
    • overflow:hiddenがviewportに適用される場合はinitial containg blockを超えて動けない
    • Window.scrollX/Y, Window.innerWidth/Heightなどのスクロール関係値は、visual viewportを元に算出されていると解釈できる
  • IE11 Mobileや最近のChromiumを除く従来の実装では、position:fixedはvisual viewportを元にレイアウトされていると考えられる

2014年やったこと(物書き業編)

MozillaのLevel 3 Committerになった

だいたい三月ぐらいに。

Mozillaにはコミッタにアクセス権限がレベル形式で設定されていて、レベルに応じてパッチをlandできるリポジトリに制限がある。Core ProductsであるFirefoxなどのリポジトリmozilla-central/inbound/fx-teamには)に投入するには、Level 3が必要。Githubに置かれてるリポジトリは、ものによって微妙に異なるけど、概ね、この規約に基づいている。

アクセスレベルが足りない場合は、パッチのレビューが終わった時点で、だれかコミット権を持っている人にlandingを頼む必要がある。が、自分からアクティブにメンターを探さないといけないわけではなくて、だいたいは該当bugにcheckin-neededフラグを立てておけば、モジュールオーナーだったりが見つけ次第landしてくれる。

自分は2012年ごろにLevel 1は取っていて、Mozillaのビルドサーバーとテストインフラを使えるようにはなっていたんだけど、(レビューをもらっても)landが自由にできないので結構苦労する時があった。複数人が現在進行形で触っている領域へのパッチなどの場合、こちらのパッチが先にレビュー完了しているのに、checking-neededに気付かれずに、他の人のパッチがlandされた結果、conflictを解消して再度やり直したり…… まあダルいよね。

というわけで、Level 3 Committerを取りました。これで自由に(もちろんr+済みの)パッチをlandできるようになりました。Level 1のときはtry-serverを使えるだけだったのでコミッタと名乗るのにちょっと抵抗があったけれども、これで胸を張ってコミッタと名乗れるようになりました。

Servo関係

色々プルリクを投げた。

一番わかりやすいあたりだと、SpiderMonkeyGCとの統合周りのデザインについてのドキュメントを書いたり、DOMまわりの細々とした変更を行ったり。主にscript関係をいじっていた感じ。結果、DOM bindingに関する知見がそれなりに増えた。

Servoのissue, pull requestは全部(流し読み含めて)目を通しているので、ほとんどのコードに何が入ってるのか大きいトピックは知っているけど、細かくコードレベルでは結構微妙。なので、2015年の抱負としては、もっと色々やっていきたい

Servoのpeerになったり、script以下のreviewer業始めたり

Githubの仕様上、issueを閉じるには権限が必要で、閉じられなくて困ってた旨をircで愚痴ってたら、peer権限もらった。モジュールオーナーというわけではないけれども、編集権限持ちです。ある種のコミッタ業(Servoの場合はpull requestをbotが自動でマージするので、コミッタ権限がそこまで重要ではないのだけれども)。

それと、主にscript以下のreviewer業も始めることになりました。とりあえずgood-first-bug級の簡単なパッチについては問題なくレビューできる感じだったり(たぶん)

余暇としてのコミッタ業

やっぱり、それなりに難しい。issueとか見忘れるとすぐに溜まる。ここらへん、もう少しなんとかうまくやれんかなというのが来年のチャレンジになりそう。

コミュニティ的な何か

Mozilla Japan様に会場を毎回お世話になった(ありがとうございます)。

Rust Language関係

Rust Samuraiをやったりした。

コミュニティ運営業は個人的にそこまで重点を置いていないのと、そろそろ言語の構文とかコンセプトじゃなくて、何ができるかとかやったほうがいい気はしているので、来年もコミュニティ業やるかのモチベーションは微妙だったり。

ブラウザエンジン先端観測会

自分の問題意識として「ド濃密な会が少ない」というのがあって、少ないならば自分でやるしかないメソッドでやった。

幸いにして好評だった模様で、次回開催について聞かれるものの、これが結構難しい… 理由は

  • 半年スパンでポンポンとエンジンが面白いことを幾つもやるわけではない
    • やっていても、とっつきやすい題材ではない。
  • speakerを探すのが大変

な辺り。前者に関しては、「わかるやつだけわかればいい」を突き進めればいいんだけど、それでも後者は難しい問題だったり。

他に面倒臭いのはマッチングなんだけど、長くなるので今回は省略する。

LL Diverに出た

縁あって、LL Diverの「帰ってきただめ自慢」のRust枠で登壇しました(資料)。

言語そのものとしては、当時はまだ1.0のリリース時期も見えなかったのと、マルチパラダイム志向でだいたいの機能を持っていたのと、常に変更し続けるスタイルなので悪いところがあってもそのうち直る可能性が非常に高いので、特にダメ自慢するところはないだろうと踏んで、主にエコシステムをダメ自慢することに。まあ、処理系が開発中でマイルストーンしか出てない言語なんで、エコシステムもへったくれもないわけですが;

その後、codegenについてはparallelに動作するようになってきたり、cargoがRust界デフォルトになってきたり、シングルスレッド性能も一部ベンチだとC++と同等を示す程度になってきましたが、相変わらずライブラリだけは少ない。ここらへんは1.0リリース後に期待したいところ。FFI bindingだけならば割と簡単にかけるので結構すぐに揃う気はする。フルRustはその後でしょうな。

2015年に向けて

通年としては

  • もう少しServo業うまくやりたい
  • 論文とかもちょくちょく読んでいきたい

短期的には

  • Rust 1.0リリース記念会は、たぶん、やります
  • ブラウザエンジン先端観測会 vol.2も、やり方変えれば目処立ちそう

みたいな、そんな感じ。

Fluxとはなんだったのか + misc at 2014

はじめに

VirtualDom - なぜ仮想DOMという概念が俺達の魂を震えさせるのか - Qiita を読んでいる前提で話を進めます。

結局”Flux”なんだったのよ

詳細については過去に自分が覚え書きを書いたのでそっちを読んでいただけると良いと思うけど、あれは MVCの変形亜種に、オブザーバーパターンを乗せ、データを単一方向に流すことを規定した」ものに、Facebook命名したものでしかない。極端に目新しいものでもない。その最大の功績はアーキテクチャそのものではなく、「試行錯誤を踏む中で誰もが一度はやっていたであろう似たようなことを上手く実践法則としてまとめた上で、共通認識としての名前をつけた」こと。概念に名前をつけて共有することで、事前説明が簡略化され、本質的な問題に取り組む時間が増える。これをFacebookのブランド力でねじ伏せるように広めたことこそが重要かつ評価すべきポイントだと思う。

Virtual DOMと組み合わせて語られることが多いけれど、別にVirtual DOMがないとFluxパターンができないわけではない。StoreからViewに向けて放たれるメッセージの粒度を、「n番目にデータを追加」「n番目から削除」「n番目を変更」などのように細かく刻んでいけばViewが何のライブラリで組まれていようが成立する。メッセージ化した時点でコマンドパターンになっていくわけだしね。

ただ、メッセージを細かく刻んでいく作業は結構だるい。そこでVirtual DOMと組み合わせると、一度に全てのデータを渡しても、どうせVirtual DOMの差分検出が面倒を見てくれるので、メッセージ粒度が「データ構造に変更あり」という粗さでも大丈夫なようになるという話。そして一回のメッセージと状態の変更を紐付けるのが容易になり、状態のスナップショットも確保しやすくなり、undo操作などの実装がシンプルなものになるというだけの話。

Fluxフレームワークについて

特にフレームワークなくても実践できるだろ

なぜ私はReact.jsを使うのか

なぜ私はReact.jsを使うのか?

  • MVCで言うところのViewとなるDOMのサブツリーの生成をテンプレートエンジン的に扱いたい
  • 適切な粒度でビューコンポーネントを分割して閉じ込めたい
  • Facebookという胴元がちゃんと自分たちで使ってることへの信頼
  • ドキュメントが非常にマトモ
  • dangerouslySetInnerHTMLとかgetDOMNode()のような緊急ハッチがある

これだけ。特に最初の2つが重要で、UIを作ろうとした場合に大きな助けとなる。

Reactの発明は成立由来からしても、UIのコンポーネント化・テンプレート化をカジュアルに行える点であり、Virtual DOMは彼らにとっては速度面のトレードオフを抑え込むためのトリックにすぎない。

別に同じVirtual DOMを使うだけであればReactよりも速いライブラリもあるのだけど、そんなものはReact.jsでパフォーマンスが問題になった時に使えばいいと思ってるし、究極的には生のDOM操作を書けば解決する問題であるし、それでも遅いならその時考えればいい話なので、全く気にしていない。

Rustプログラミングにおけるデバッグ入門

これはRust Language Advent Calendar 2014の第1日目となります。

尚、本情報はRust 0.13.0-nightly@fac5a0767時点の情報を基にしております。

現実においてプログラムを書くにあたりデバッグを行わないということはありません。ですので、通常、我々はデバッグ技法というものを習得しなければなりません。

デバッガ

RustではDWARF形式のデバッグシンボルを出力できるため

といったツールの使用が可能です。WinおよびLinuxではgdbを使えばいいでしょう。OSXでは、個人的にはlldbを使えばいいと思います(尚、gdb向けの拡張スクリプトもrustリポジトリの中には存在しています)

また、DWARF形式のシンボルを用いた各種ツールチェインも使用が可能となっていますが、各種バグには注意を配る必要があるかもしれません。

log crate

デバッガを用いずとも、変数の内容などをダンプするデバッグというのも非常に有用あるのは皆さんご存知のことと思います。殊、Rustのように平行・並列実行を指向したコードを記述する場合、デバッガでアタッチしたりrrのようなツールチェインを使わずとも済ませたい・もしくは前述のツールチェインを使いことが困難なこともあると思うので、この手法にはお世話にならざるを得ません。

そこで使うのがこの log crateです。 詳細な使い方はリンク先を参照していただくとして(安定度もexperimentalですしそちらの方が確実でしょう)、概要を説明しますと、

  1. log
  2. debug
  3. info
  4. warn
  5. error

のレベルに分けてログを出力することができます。

出力にあたっては、実行時にRUST_LOG=path::to::module=log_level環境変数を設定することで、「ログのレベル」と「ログを出力する対象であるモジュール」の指定が可能です。

この指定は、正規表現および用いた複雑な表現が可能であり、/の後にwarn/bar*fooのように続けることで「ログレベルwarn以上で、正規表現bar*fooにマッチするメッセージを含むログを出力する」などのような指定を環境変数経由で実行時に指定することが可能です。

また、原則としてmacroで実装されているので、現在の実装では、debug!に関しては--cfg ndebugコンパイルオプションとしてrustcに指定した場合に、自動で無効化されます.

アサーション

Rustには実行時アサーション複数デフォルトで存在しています.

debug_assert系では、--cfg ndebugrustcに対して渡すことにより、これらのアサーションを無効にしたコードの生成が行われます。

ユニットテストベンチマーク

Rustではコード中に#[test]または#[bench]といった指定を行ってコードを記述することで、コンパイルオプションによりユニットテストまたはマイクロベンチマーク用のバイナリを簡単に生成することが可能です。

詳しくはThe Rust Testing Guideを参照するのが良いでしょう。

バックトレース

実行時に環境変数としてRUST_BACKTRACE=1を設定することで、クラッシュ時の詳細なバックトレースを得ることができます。

cargo buildrustc —cfg ndebug相当のことをする

cargoの設定のprofileで明示的に指定を行えば、対応するビルドプロファイルに反映されます。

デフォルトでは--releaseをつけた場合にrustc —cfg ndebug相当となります

まとめ

よいRustプログラミングをお楽しみください. 明日の担当予定は@omasanoriさんです。

Fluxの枠にURLルーティングを収める試行

JSer.info 200回記念祭の懇親会でざっくりアイディアだけ話していた記憶(酔っ払っていたので正確には覚えていない)なんだけど、実際に必要になったので試しに作ってみたという話。

モチベーション

Fluxパターンを用いた設計を行なっている場合というのは往々にしてSingle Page Applicationであるので、URLに基づくルーティングを要しない、純然たるアプリケーションなケースが多い。だが、アプリケーションの性質によっては、パーマネントリンク的な機能の再現をしたいことがあり、ルーティング機構が欲しかったりする。

で、そういう場合については語られてる事例をあんまり見かけなかったので、作ってみた。

デザイン

  • Storeに基本ロジックを閉じ込めるのは変わらない
  • URLに基づく履歴情報は、ユーザーインターフェースの一種と捉える。ので、Viewと考える

使ったライブラリ

基本要件として

  • URL path文字列のパースとそれに応じたルーティングの実施を行うルーティング処理
  • URL pathを書き換えるための、History API操作

のそれぞれが独立して動くようにする必要がある。 JSのルーティングライブラリは、どういうわけかBackboneとBackboneベースの派生カスタムライブラリに乗っていることが多いので、探すのに苦労した。が、幸運なことに見つかった(見つからなかったら自作せざるを得なかった)

サンプルコード

だいたいこんな感じでいいのはないでしょうか:

var crossroads = require("crossroads");
var Dispatcher = require("flux").Dispatcher;
var EventEmitter = require("events").EventEmitter;
var hasher = require("hasher");

var RoutingActionCreator = {

    moveTo: function (path) {
        RoutingDispather.dispatch({
            actionType: "Routing::moveTo",
            path: path,
        });
    },
};

var RoutingDispather = {
    _dispatcher: new Dispatcher(),

    dispatch: function (val) {
        this._dispatcher.dispatch(val);
    },

    register: function (callback) {
        this._dispatcher.register(callback);
    },
};

var RoutingStore = {
    _emitter: new EventEmitter(),

    EVENT_CHANGE: "RoutingStore::change",

    dispatchToken: null,

    setup: function () {
        crossroads.addRoute("/bar", () => {
            // route毎に必要な処理
        });

        crossroads.addRoute("/foo", () => {
            // route毎に必要な処理
        });

        // 各route毎の処理が終わったら, storeのchangeイベントを起こす
        crossroads.routed.add((path) => {
            this.emitChange(path);
        });
    },

    addChangeListener: function (callback) {
        this._emitter.addListener(RoutingStore.EVENT_CHANGE, callback);
    },

    emitChange: function (path) {
        this._emitter.emit(this.EVENT_CHANGE, path);
    },
};

RoutingStore.dispatchToken = RoutingDispatcher.register(function(payload){
    switch (payload.actionType) {
        case "Routing::moveTo":
            var path = payload.path;
            crossroads.parse(path);
            break;
    }
});


var URIHistoryView = function () {
    this.init();
};
URIHistoryView.prototype = {

    init: function () {
        RoutingStore.addChangeListener(this.onChange.bind(this));

        // URLの変更に応じてActionを起こす
        var parseHash = function parseHash(newHash){
            RoutingActionCreator.moveTo(newHash);
        };
        hasher.initialized.add(parseHash);
        hasher.changed.add(parseHash);

        hasher.init();
    },

    onChange: function (path) {
        // Storeのchangeイベントのコールバック変更のため, URLの変更だけする.
        hasher.changed.active = false;
        hasher.setHash(path);
        hasher.changed.active = true;
    },
};

function main() {
    RoutingStore.setup();
    new URIHistoryView();
}

上のコードに書いてない点

URLに紐づく具体的なViewの生成箇所について

上のサンプルコードだと明示していない。Store内の各ルーティングのコールバック内で、適当にControllerって名前をつけた関数を呼んでもいいかもしれない。Fluxの原則に外れるけど、まあどうせ生成するViewの粒度なんて大きなものだし、枠外に外れるのはここだけだし、XXX とかつけておいてドキュメントも書いておけば良いだろう。良くないか。普通にStoreから発行されるchangeイベントを監視するのもいいかもしれない

ルーティングを回避して、URLだけ更新したい場合

専用のメッセージ作ってルーター呼ばずにそのままStoreのchangeイベントを発行してViewに伝えりゃ良いじゃろ

まとめ

本サンプルコードが動かなくても気にせず、雰囲気で行きましょう。

React.js読んだ感想とかFluxアーキテクチャとか

この間、virtual DOM読んだついでにReactも読んでみた。 役に立つ話を書くつもりは一切無くて、感想だけ。

React.js

  • 定数だけのファイルとか(まさに*hな感じ)、デザインや使い方について書かれたコメントなど、全般的にC++に類似するstaticなコードの流儀で書かれている。
    • イケイケな「動的言語最高!」って言わんばかりのJSのコードではなく、明らかにC++の巨大プロジェクトなどに近い。Mozillaの中のJSとか、XPCOMな箇所で少し古いものとかにデジャブを感じる。
    • もっとも、巨大かつ基盤になるようなプロジェクトはこうせざるを得ないし、小手先のreadable性に頼らずに真っ当に巨大ソフトウェア開発してる趣が個人的な心象は最高に良い。
    • ただし、本来静的解析フェーズで片付けるべき箇所もカバレッジのためにテストを記述している箇所が有り、ちょっとそこはイケてないと感じるかもしれない……
    • まあFlowって静的型解析ツール出すらしいし、それに期待でいいんじゃないかな
  • Mixinが多用されていること自体は別に構わないのだが、フラットな混ぜ込み方ではなくて、ときどき菱形継承とか、別のところから引っ張ってきたメソッドFunction.call()するとか、手前いい加減にしろ感のあるコードがあるのが実につらい
    • こういうことやるならRustかC++で良かった感してくる
    • いいからとにかくRustの型システムとTraitをJSによこせ感
    • こういうところが、あんまり何度も読みたくは無い心象を受ける。
  • Virtual DOMと謳ってはいるが、内部的にはただのデータ構造があるだけ。
    • vtreeみたいにわかりやすい形では存在しない
    • まあReact IRとそれを取り扱う機構をVirtual DOMって呼んでるだけですな
  • 内部的に処理をアトミック操作にするために、transactionって名前でバッチ処理モードを行っている
    • で、このバッチ戦略は、プラガブルに拡張可能だったりするのが面白いよね!

Fluxアーキテクチャについて

  • 全部メッセージパッシングで片付けるの、夢というか正道感ありますよね
  • 彼らのMVCのネガティブケースのたとえが「よくあるMVC」とは、あんまり思いたく無い
  • が、ある時点で、同時に任意の個数のビューおよびコントローラが存在している場合(n個前のビューのキャッシュとかね)、currentのビューで発生した変更を、他すべてに伝えるのはまじめにイテレーションすると正直ダルい。
    • そこに非同期性が入ってくると、メッセージ投げてコールバックする以外の解決策は現実的ではない
  • だったら、一発メッセージを発行して、購読しているインスタンスが勝手に変更してくれる方が良いというアプローチだと理解。アーキテクチャとしては素直だよね。
  • まー、次第に自然にこんな感じの設計になりますわなーって感想

まとめ

  • コードは読みたくないが、使いたいのがReact.js
  • とはいえ、テストまで含めてなんでもかんでも全部入りアプローチってAngular以外無いのがつらいよねー
  • いい加減に新しいライブラリ探しまわるのは本意じゃないのでReactで当面片付けたい