History APIの本当の恐ろしさを

教えてやる


文殊堂( @monjudoh )

お前誰よ

  • Warlock(黒魔術師)やってます

  • お仕事でJSの黒魔術書いたりしてます

昔からあるAPI


  • history.back
    • 何かの操作でブラウザバックさせるみたいの
  • history.forward
  • history.go

最近導入されたAPI

  • history.pushState
  • history . replaceState
  • popstate event

    事例

    • pjax
      • Githubで使われている
    • Rails4のturbolinks


    こういう奴

    1. サイト内のリンクをクリックする
    2. 画面遷移は止める
    3. リンク先のコンテンツを表示するのに必要なデータをAjaxで取得して表示
    4. 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
      • 履歴の圧縮

    ご清聴ありがとうございました

    Made with Slides.com