(この資料は専用のプリプロセッサで動くことを全体にしたドキュメントです)
mizchi / Increments, Inc.
@ React Meetup #1
- Kobito for windowsをプレビュー用にちょっといじったもの
- mizchi / 竹馬 光太郎
- Qiitaの会社より
- Reactバズらせ手
もう話すことない!
エッジケースと、なんでそのライブラリ作ったか、みたいな話します
Reactで一つのアプリケーションを仕上げるにあたり、直面した問題と解決方法
既存のkobitoの機能(markdown preview + Qiitaへの同期) + Markdownエディタとしての使い勝手向上
- 画面数は少ない
- 閲覧/編集/設定/ログイン
- 画面ごとのロジックの密度が高い
- テキストエディタ
- Markdown Preview
- 記事一覧のソート/検索
- Qiitaとの同期/コンフリクト検知
- 開発開始から4ヶ月
- およそ12000行
- エンジニア 1+α人
- デザイナ 0.5人
- React
- Electron(旧AtomShell)
- CodeMirror: テキストエディタ
- IndexedDb: ストレージ
- Qiita API v2: サーバー
Real World Virtual DOM // Speaker Deck
- ↑は2ヶ月前時点のもの
- Fluxの設計や思想、Ardaについては↑で
- クローズドβテスト中
- プロトに1週間
- 設計に2ヶ月
- フィードバックからの品質改善
- バグ潰し/ブラッシュアップ
- (来月にはリリースするかも?)
- ライブラリ選定/プロトタイピング/学習 のフェーズ
- 結局フレームワークからストレージ層まで自分が求めるのなくてライブラリを自作した
(去年の11月時点ではReact/Fluxの情報が少なかった)
- 画面遷移を表現できるFluxが少ない
- ReactのdangerouslySetInnerHtml
- (jsx辛い)
- おそらくreact-routerが一番使われてる
- 自分は気に食わなかったので自作した
- mizchi/arda
- 設計意図については Real World Virtual DOM // Speaker Deck を参照
router = new Arda.Router(Arda.DefaultLayout, document.body)
router.pushContext(MainContext, {}) # Main
.then => router.pushContext(SubContext, {}) # Main, Sub
.then => router.pushContext(MainContext, {}) # Main, Sub, Main
.then => router.popContext() # Main, Sub
.then => router.replaceContext(MainContext, {}) # Main, Main
.then => console.log router.history
前後の状態をキャッシュして画面をpush/popする
- HTMLのリアルタイムプレビューでdangerouslySetInnerHtmlを使うと頻繁に ブラウザが固まる
- ReactにHTML文字列をそのまま挿入する唯一の方法
- 名前の通り危険なので推奨ではないAPI
function createMarkup() { return {__html: 'First · Second'}; };
<div dangerouslySetInnerHTML={createMarkup()} />
- markdownをプレビューするというアプリの性質上、プレビューが必須
- 公式のサンプルにもmarkdownプレビューはあるが、巨大なドキュメントを突っ込むといとも簡単に壊れる
- React JS Markdown Editor - JSFiddle
- iframeに突っ込む
- 毎回クリーンしてから表示
どちらもパフォーマンス上の問題を抱える
- markdownをReactElementに変換して挿入
- Reactで差分描画することで 巨大なドキュメントでも高速に描画できるようになるはず
- mizchi/md2react
md2reactの使用例
global.React = require('react');
var md2react = require('md2react');
var md = '# Hello md2react';
var html = React.renderToString(md2react(md));
出力
<div data-reactid=".14qrwokr3sw" data-react-checksum="20987480"><h1 data-reactid=".14qrwokr3sw.$_start_root_0_heading"><span data-reactid=".14qrwokr3sw.$_start_root_0_heading.0">Hello md2react</span></h1></div>'
//'<div data-reactid=".58nba97pxc" data-react-checksum="-55236619"><h1 data-reactid=".58nba97pxc.0"><span data-reactid=".58nba97pxc.0.0">Hello</span></h1></div>'
- mdast でASTにパースする
- ASTをトラバースしてReactElementに変換
- HTML埋め込み記法はプリプロセスで別途ASTに変換(thx @uasi)
Componentがpropsに対して冪等な設計すると、やっぱり一部重いのでキャッシュ戦略が必要だった
- componentWillUpdate(object nextProps, object nextState) を使う
- 前後のプロパティで比較してキャッシュ破棄/更新
boolean shouldComponentUpdate(object nextProps, object nextState)
この関数をオーバーライドしてfalseを返すとComponentの更新をキャンセルする
- かなりJS寄り
- デザイナに気軽に覚えてもらえるようなテンプレートエンジンがほしい
- 「Arrayをmapしてリスト要素作って変数にいれたのを別の箇所で展開する」のをデザイナに要求するのきつくないですか?
- jadeテンプレートを拡張したもの
- hamlとかslim知ってると問題なく使える
doctype html
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) {
bar(1 + 5)
- 複数行のコードの入力ができない
- 生JSしか書けない
- React固定
複雑なコンポーネントを書き始めるとインラインJSが書きづらいのがかなり辛い
テンプレートエンジン作り始めた
WIP
- ReactElementを吐く関数をテンプレート(一種のAltJS)
- インラインCSSが書きやすい
- インラインコードはBabelでプリプロセスする
- 任意なVirtualDOM実装を吐く(React/Mithril/Deku/virtual-dom)
- インラインHTMLからSCSSを吐く
- React.PropType を吐く
.main.container&mainCotnainer(
data-id = 'this-is-id'
) {
background-color: #eee
width: 640px
height: { 40 * 12 }
font-size: 1em
}
// text
h1 This is a title
| expand with span
span = @greeting
- let onClick = () => console.log('clicked);
div(onClick=onClick)
インラインコードはbabelでプリプロセスされる
パーサーから作っててエッジケースが潰れてない
とりあえずreact-jade倒すぞ!!
Reactを4ヶ月使った感想まとめ
必要なパーツが足りない/or既存実装が気に食わないので、結局全部自分で作った
- ポテンシャルの割に個々のユースケースの差を吸収できるほどのライブラリは、まだ揃っていない。
- ↑自分で作るチャンス!!
- 選択肢が少ないことが我慢できれば使える
- 自分は我慢できない
- 趣味みたいなもん
- 表面的にはAPIが少ない
- ヘッドレステストができる
- (jQueryとかどうでもよくなる)
- サイズが大きい(.min.jsで114kb)
- 一般的なjQueryと混ざらない
- ある程度以上複雑(一定以上の頻度の書き換え)じゃないとメリットがない
というわけで普通のウェブサイトでもランタイムが小さいmithril/deku等を使えるようにしたい!