History APIの本当の恐ろしさを
教えてやる
文殊堂( @monjudoh )
お前誰よ
- Warlock(黒魔術師)やってます
- お仕事でJSの黒魔術書いたりしてます
昔からあるAPI
- history.back
- 何かの操作でブラウザバックさせるみたいの
- history.forward
- history.go
最近導入されたAPI
- history.pushState
- history . replaceState
- popstate event
- …
事例
- pjax
- Githubで使われている
- Rails4のturbolinks
こういう奴
- サイト内のリンクをクリックする
- 画面遷移は止める
- リンク先のコンテンツを表示するのに必要なデータをAjaxで取得して表示
- URLをリンク先のものに書き換え
何が出来る?
- pushState/replaceState
- URLの書き換え
- 画面遷移を伴わない
- これまでのAPIではhash fragments以外の変更はできなかった
- pushState
- ブラウザバック/フォワード履歴(以降"履歴")への新規エントリの追加
- popstate event
- ブラウザバック/フォワードの検知
それで終わり?
話変わって
JSで使えるクライアントサイドでのデータ永続化
どれをご存知?
- cookie
- WebStorage
- localStorage
- sessionStorage
- IndexedDB
- FileSystemAPI
- WebSQL
- userData behavior
話し戻って…
APIを見てみよう
- history . pushState(data, title [, url ] )
- history . replaceState(data, title [, url ] )
- history . state
dataって何?
stateって何?
- 永続化される状態です
history.state
- 履歴の各エントリは状態を保持することができる
- 状態はstructured clone可能なもの
- 状態は保存される
- 以下の操作後に状態は消えない
- 別documentへの遷移
- リロード
- ブラウザによっては以下の操作後も状態は消えない
- ブラウザ再起動
structured clone
- HTML5で使えるデータ複製
- postMessageでのデータの受け渡しの等にも使われる
- JSON.stringify/parse前後で変化しない物
- primitiveも含む
- Date,RegExp等の一部の古くからあるbuiltin型
- ArrayBuffer,Blob等バイナリデータの型
History APIでは
- history . pushState/history . replaceStateで渡されたデータがstructured cloneアルゴリズムで複製され保存される
- history.state,PopStateEvent#stateで取得できる
stateの使いどころ
- サイトではなくブラウザ上でのアプリを作る際に使う
- 履歴はブラウザだけのものではない
- iOSアプリのUINavigationController
- 例えばタブの数だけぴったりUINavigationControllerがあるiOSアプリを想定するとタブブラウザみたいではないか?
- アプリでサブ画面の表示等を履歴の1エントリとして扱う
- サブ画面の持つ状態の一部をstateに保存する
- pjaxのようにURLを書き換えるだけではだめなのか?
- アプリの持つ状態を何でもかんでもURLに反映させるのはどうかと
履歴・stateの難点
- 現在位置が分からない
- history.lengthで履歴の全体サイズは分かるがどこにいるか分からない
- back/forward出来るか分からない
- 現在のdocumentの範囲が分からない
- 別のページから遷移してきた/別のページに遷移してbackしてきた時にどこが境界か分からない
- そもそも現在位置が分からないので分かっても意味はない
- 現在位置以外のstateを取得できない
- セキュリティの都合は分かるが、同一document内でも参照できないのはどうなのか
BeautifulHistory.js
- History APIの弱点を補い活用するためのライブラリ
- ViewControllerのtypeと対応するfactory等を登録し、
以下のようにしてサブ画面の表示等を行う。
BeautifulHistory.push(type,options);
BeautifulHistory.replace(type,options); - ユーザ操作のback/forwardを検知してhide/show callbackを呼ぶ
- document内の範囲であれば状態をJS側でも持つ
- 現在位置でなくても参照できる
- ブラウザリロード時にdocument内の範囲の状態をリストアする
API
- currentIndex
- 履歴のBH管理下の部分での相対位置
- maxIndex
- 履歴のBH管理下の部分での末尾
- built-in type 'forceBack'
- 遷移してきたら即座にbackするというtype
- これをpushすることでforward方向に未管理の履歴が存在しないことを保証できる
- backToPreviousDocument
- 現在のdocumentに遷移してくる直前の位置までback
- collapse
- 履歴の圧縮
ご清聴ありがとうございました
History APIの本当の恐ろしさを教えてやる
By monjudoh
History APIの本当の恐ろしさを教えてやる
- 8,671