WebComponentsでSPA風遷移をする

## 解決したい課題
![atag](https://github.com/takinaga-dev/spa-router-sample/blob/master/atag.gif?raw=true)
この赤い部分がwebComponentsなんですが、上の動画みたいにwebComponentsからリンクをクリックしたときspaみたいな遷移をしたい。

## 詳細
[webcomponents](https://developer.mozilla.org/ja/docs/Web/Web_Components)とは、再利用可能なカスタム要素を作成し、ウェブアプリの中で利用するための、一連のテクノロジーです。

今フロントエンドは複数あるフレームワーク毎にロックインされており、プロジェクト選択時に今までと違うフレームワークを選択してしまえば過去の資産の再利用は難しく、その都度再開発が必要になります。
webComponentsで共通部品を提供できればあらびっくり、VueでもAngularでもReactでもjQueryでも使いたいwebComponentsを読み込んでおなじみのHTML tagを書けば、複雑なロジックを含んだ部品を爆速で組み込むことができるんですね。

さてそんな便利なwebComponentsですが、一点不満がありwebComponentsで実装した部品からページ遷移するときに、仮にそれが同じアプリケーション内での遷移であってもSPAのような遷移をしないというところです。

AngularならrouterLink,Vueはrouter-link,ReactならRouter path(React使ったことないから違うかも)を使って遷移します。
これらの機能はあくまで各フレームワーク内の機能であり外部から持ってくるwebComponent内からは利用することができません。

例えばheaderのwebComponentsを用意して、会社の複数のプロダクトは共通のheaderを使わせたい場合を考えましょう。
構成はheaderの下に別にメインコンテンツ領域が存在し、headerのリンクをクリックすることでメインコンテンツにクリックした内容の画面が描画されるというごく一般的なページレイアウトです。

さて当然headerは例外を除いて常に表示されます。そのためheaderにはプロダクトのロゴが用意してありクリックしたらトップページに遷移してくれると嬉しいですよね。

ただwebComponentsでheaderを用意すると、SPA遷移ができないため通常の`<a>`tagのリンクでの遷移となります。
せっかくSPAで読み込んだのに、また重い初期ロードが発生してしまうわけですね。辛い。

今回、どうにか無理やり頑張ってこのSPA的遷移をwebComponentsで使うことができるようにしてみましょう。
というお話です。

## 説明すること
- WebComponentsでSPA風画面遷移をできるようにする方法とコード

## 説明しないこと
- WebComponents自体とか使い方とか
- Angularのディレクティブとかそういう話

## 今回のアプリケーションについて
https://github.com/takinaga-dev/webcomponentsInAngularSample
にコードあります。リポジトリ名の通り、Angularのサンプルアプリにwebcomponentsのフッターを利用しています。
ロゴをクリックするとトップ画面に遷移するという単純なアプリです。

### a tagでの遷移

まず、a tagを利用した通常の遷移について見てみましょう。
![atag](https://github.com/takinaga-dev/spa-router-sample/blob/master/atag.gif?raw=true)
見てわかるようにAngularアプリ内の画面遷移(/sample)は画面の再ロードが発生していませんが、webComponentsであるfooter上のリンクからトップへ遷移しようとすると再ロードが発生していることがわかります。

### SPA風遷移

次に今回紹介するSPARouterディレクティブから遷移すると
![spaRouter](https://github.com/takinaga-dev/spa-router-sample/raw/master/spa.gif)
再ロードを発生させずに、まるでSPAのように画面遷移が出来ていることがわかります。

## 利用方法
[ここ](https://github.com/takinaga-dev/spa-router-sample)からソースとってきて
[ここ](https://angular.jp/guide/attribute-directives)を参照してつかってみてください。


## コード(Angular側)

```app.component.html
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <a routerLink='/sample'>to sample page</a>
  <router-outlet></router-outlet>
</div>
<router-outlet></router-outlet>
<custom-footer></custom-footer> <!-- ←これが今回利用するwebComponents -->
```

`custom-footer`が今回こちらが作成したwebcomponentsです。
共通の処理としてロゴをクリックすると、そのサイトのトップページに遷移するようになっています。

## コード(WebComponent側)
### Aタグを利用する場合
```custom-footer.html
<div [ngStyle]="{'background-color': 'red'}">
  <!--クリックできるけど再ロード-->
  <a href="/">ロゴ(トップへ戻る)</a>
  <!--
    <a routerLink="/">ロゴ(クリック出来ない)</a>
  -->
</div>
```

通常webcomponents内からではrouterLinkなどを使った画面遷移は出来ません。
注)webcomponents内で複数のページを持っている場合は、遷移は可能です。

### SPARouterを利用する場合
```custom-footer.html
<div [ngStyle]="{'background-color': 'red'}">
  <p appSpaRouter='/'>ロゴ(トップへ戻る)</p>
  <!--クリックできるけど再ロード
  <a href="/">ロゴ(トップへ戻る)</a>
  -->
  <!--
    <a routerLink="/">ロゴ(クリック出来ない)</a>
  -->
</div>
```

コード全体は
https://github.com/takinaga-dev/spa-router-sample
を見てみてください。

### 解説
さてこの[SpaRouterディレクティブ](https://github.com/takinaga-dev/spa-router-sample/blob/master/src/app/spa-routerlink.directive.ts)では何をやっているかというと、

単純にブラウザの戻るとブラウザの進むを実行しているだけです。
これでこのような遷移方法が実現できるのは、SPAフレームワークの遷移方法がHTML5のhistory APIを利用しているためです。
[history APIのgo/back](https://developer.mozilla.org/ja/docs/Web/Guide/DOM/Manipulating_the_browser_history)を利用するとブラウザはpushStateイベントを発行するので、SPAフレームワークがそれを監視して、AngularでいうrouterLinkディレクティブと同じ挙動をgo/back APIがコールされることをトリガーに実現することが出来ます。

### 注意点
基本的にwebComponentsを利用してSPA的な挙動をしたいという事自体webComponentsの利用用途として外れているのかなぁと思ったりします。(ルータの記述がSPAアプリケーションとwebComponentsと分散する。webComponentsの部品化がうまく出来ていない。etc)

あくまでSPA的な遷移をしたいや再ロードを防ぎたいという場合、こういう方法もあるよという一案として読んでいただければと