Github pagesでハッシュなしSPAを作る[Vue.js + vue-router]

# Results

私のポートフォリオページでこれを実現しています．

[Demo](https://hiyoko3.github.io/)

# Introduction

Single Page Application(SPA)はファイルの実体が存在していないため，ブラウザをリロードすると404を返されてしまいます．

この問題を解決するためSPAでは基本`#/`を用いているのですが...


`#/` <- こいつすごい気になる！

[SPA](https://qiita.com/takanorip/items/82f0c70ebc81e9246c7a) を構築している殆どの開発者がこう思ったのではないでしょうか？


このハッシュを消す方法はいくつか存在します．

ApacheやNode.js，Nginxなどは[公式](https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations)を参照すると良いかと思います．

しかしながら，この中にはGithub pagesについては記載されていませんでした．
本記事はGithub pagesでもハッシュなしのSPAを実装する方法をご紹介します．

# How to

[ここ](https://github.com/rafrex/spa-github-pages)を参考にプログラムを修正しました．
SPAに必要なのは遷移先を示すURLなので(`https://[:domain]/[:path]`の`[:path]`),ページリロード時に`:path`を取得，その後パラメータとしてURL　に`:path`を追加して`index.html`へリダイレクトさせてあげます．

## Preparanation

`vue-router`の __[history mode](https://router.vuejs.org/guide/essentials/history-mode.html)__ を有効にします．

```javascript
const router = new VueRouter({
  mode: 'history', // <= ここ
  routes: [...]
})
```

## Example

以下の構成でSPAが構築されているとします．

```
https://hoge.com/
https://hoge.com/foo
```

この時，`https://hoge.com/`から`https://hoge.com/foo`へ遷移後，ページをリロードすると404エラーが返ってきます．

## Methods

まず，`404.html`を作成します．
Github pages はページが見つからないと作成した`404.html`を表示するためこの特性を利用して以下のscriptを記述します．

- 2018/2/2 Added:　[narugitさん](https://qiita.com/narugit)からご指摘がありました部分を修正いたしました．

```javascript
let secmentCount = 1; // [2018/2/2 Added]: 変数が抜けていると指摘がありましたので，追加しました．
let l = window.location;
let port = l.port ? ':' + l.port : ''
l.replace(
    `${l.protocol}//${l.hostname}${port}/?p=${l.pathname.split('/').slice(0, 1 + segmentCount).join('/')}`
)
```

これは`https://hoge.com/foo`というURLが与えられていれば，`https://hoge.com/?p=/foo`という形で`index.html`へリダイレクトするスクリプトです．

続いてindex.htmlに以下のスクリプトを追加します．

```javascript
(function (l) {
    if (l.search) {
        var q = {};
        l.search.slice(1).split('&').forEach(function (v) { // ?p=/fooの分解
            var a = v.split('='); // 'p=/foo' となっているはずなので=で分割
            q[a[0]] = a[1]; // {p: '/foo'} 形式に修正
        });
        if (q.p !== undefined) {
            window.history.replaceState(null, null, l.pathname.slice(0, -1) + (q.p || '')); // https://hoge.com/fooへ遷移
        }
    }
}(window.location))
```

以上で完了です．
index.htmlに入れるスクリプトは__必ずVue.jsを読み込むスクリプトの前__に書いてください．
`<head>`内に書いておけば大丈夫かと思います．

# Coclusion

遷移時にパラメータを付与したい場合などは[ここ](https://github.com/rafrex/spa-github-pages)にパラメータ付きの場合のプログラムが掲載されているので利用すると良いかと思います．

参考にまでに私の[コード](https://github.com/hiyoko3/hiyoko3.github.io)を載せておきます．
`src`を参照してください．

以上！

# P.S. [2018/07/07 Added]

`webpack`と`Webpack-dev-server`を利用しているとネストしたルート(`https://hoge.com/foo/bar` など)でリロードすると白紙のページが返るくる方がいらっしゃると思いますが，
これは相対パスでコンパイルされたファイルを読み込もうとしているため発生しているエラーです(__`<script src=bundle.js></script>`__となっている)．

`webpack` の設定を少し変更するだけで解決します．

```javascript
webpack.output.publicPath = '[:publicPath]'
```

上記を追加すると`[:publicPath]`から先のディレクトリを参照します．
例えば `[:publicPath]`を`/`にした場合， __`<script src=/bundle.js></script>`__ となります．
これを追記することで`https://hoge.com/foo/bar`でリロードしても問題なく表示されるはずです．

# References

[Stack Over Flow](https://stackoverflow.com/questions/34620628/htmlwebpackplugin-injects-relative-path-files-which-breaks-when-loading-non-root)
[SPA github pages](https://github.com/rafrex/spa-github-pages)