5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

サーバなしで使えるハッシュタグ(#)のルーティングでSPAに挑戦してみた

Last updated at Posted at 2018-02-03

背景 SPAのルーティングって何?

jsnoteでサンプルコードを共有するときに, 普通のWEBページみたいにURLで渡せると便利だなと思いました. GitHubとかコード一つ一つにURLついているし, あんな感じにできたらいいなと思いました. でもどうやったらできるかわかりませんでした. サーバがテンプレートを返しているとしたら, GitHub Pages(静的ファイルだけ配信できる)で真似することはできないけど, なんとかならないかなぁと思っていました.

ネットで調べていたら, 自分がやりたいことはSPAのルーティングだと分かりました. そして2つの記事を見つけて参考にさせていただきました.

https://www.slideshare.net/ushiboy/spa-76170499
https://www.tam-tam.co.jp/tipsnote/javascript/post9911.html
これでできる気になりました.

選択肢

(1) サーバなし VS あり
SPAでルーティングするためには, サーバなしでいける#タグと,サーバが必要だけどURL書き換えてリダイレクトする方法がある. どちらにするか.

(2) # VS ?
サーバなしで行く場合,#タグだけでなく, ?を使ってGETのパラメータを設定することもできる.どう使い分けるか.

試行

(1) NginxでURL書き換えてリダイレクトすれば, #タグなしでGit Hubみたいなページを作れるんじゃないかと思っていました. でも, Git Hubのgh-pagesで公開するにはサーバは使えない.
そんな時, 上の記事で#タグの存在を知って, サーバなしでできると分かったので, 迷わず#タグを選択しました.

サーバでURL書き換える方法は, きっとこういる感じに書けばいけると思います.
/jsnoteで来たリクエストを/にリダイレクトするサンプル(/index.htmlが呼ばれる)

default.conf
 location / {
    root /usr/share/nginx/html;
    index index.html;
  }
  location /jsnote {
    rewrite /jsnote/(.*) / last;
  }

(2) # VS ?
今回, #タグをつけると, #以下を無視してHTTPリクエストを送ると知りました. でも, それってGETのパラメータを指定する ? でも同じではないかと思いました.下の二つは, SPAでルーティングするときに何が違うのでしょうか.

localhost/#draw=1
localhost/?draw=1

2通りを試行してみた結果, 次のことが分かりました.

  • #の場合
    JavaScriptで動的に#以下を書き換えてもページがリロードされることはなく, popstateに登録したイベントだけが実行される.
  • ?の場合
    JavaScriptで動的に?以下を書き換えるとページ全体がリロードされる. パラメータ変えるたびにリロードで画面がちらつく. GETのパラメータはHTTPリクエストとして送られるのだから, 当然かもしれない.

どちらでも同じページにはいけますが, やはり?はGETとしてパラメータをサーバに送る用途で使うと再確認し, #の方が画面がちらつかないし速いので#を選択しました.

実践

#以下のパラメータを扱うために4つの関数を作りました.
(1)parseParam: パラメータをパースしてMapオブジェクトに入れる.
(2)convertMapToHashString: パラメータを#タグようの文字列に戻す. (3)と(4)で使用.
(3)addParamToHash: パラメータを追加して#タグを書き換える.
(4)addParamToHash: パラメータを削除して#タグを書き換える.


const parseParam = (hash)=>{
  if(hash){
    const param = hash.split("&")
      .reduce((pre,current)=>{
        const item = current.split("=");
        const key = item[0]
        const value = item[1]
        pre.set(item[0],isNaN(value)? value: parseFloat(value)); 
        return pre;
    },new Map());
    return param
  }
  else {
    return new Map();
  }
};

const convertMapToHashString = (map)=>{
  let paramArray = [];
  for(let [k,v] of map){
    paramArray.push(k+"="+v);
  }
  const paramString = paramArray.join("&");
  //console.log(paramString);
  return paramString 
}

const addParamToHash = (key,value)=>{
  const param = parseParam(location.hash.slice(1));
  param.set(key,value); 
  location.hash = convertMapToHashString(param) 
};

const removeParamFromHash = (key) =>{
  const param = parseParam(location.hash.slice(1));
  if(param.has(key)){
    param.delete(key)
    location.hash = convertMapToHashString(param) 
    return true;
  }
  else {
    return false;
  }
};

使い方

parseParamを使ってparamにパラメータを代入する.

const param = parseParam(location.hash.slice(1));

ユーザがパラメータを変えたときに,

   addParamToHash("drawCheckBox",1);

もしくは,

   removeParamToHash("drawCheckBox",1);

という感じで#タグを書き換える.

戻るボタンに対応

戻るボタンに対応するのが大変でした. リロードボタンを押してから, 戻るボタンを押すと挙動不審になりました.
そこで, この記事を読んでhistory APIを勉強させてもらいました.

わかったこと
(1)パラメータ操作する内容は関数にしておいて, 最初に全部popstateに登録すると,リロード&戻るボタンしてもそれっぽく動く.

const jsnoteInitialize = ()=>{//やりたいこと全部書く
  keyBinding();
  fontSize();
  getCode();
  checkBox();
};

jsnoteInitialize();
window.onpopstate = jsnoteInitialize;

(2)以降, JavaScriptで動的に#タグを書き換えると, popstateで登録した関数が呼ばれるので, #タグを書き換える前に, 個別の関数をpopstateに登録するとよい. 自分で関数を呼ぶと2回呼ばれることになるから, 自分では呼ばずhashchangeイベントに任せる.

window.onpopstate = keyBinding; //hashの変更前にpopstateに関数を登録
addParamToHash("keyBinding",keyElem.selectedIndex); //hashchangeで自動的にkeyBindingが呼ばれる

まとめ

#タグにパラメータを入れて, 処理内容をpopstateに登録して, 動的に#タグをJavaScriptで書き換えれば, ルーティングできる.

参考

  • jsnote: runボタンを押すとPlotlyで棒グラフを描きます.
  • ルーティング追加したjsnoteのコードはこちら
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?