JavaScript
SPA
vue.js
SinglePageApplication

Vue.jsでアプリケーションを開発するときによくある質問集

概要

Single Page Applicationを実装するにあたって、気になることの質問・回答集。
ここで言う「Single Page Application」とは、画面遷移(リロード)をせず、1ページ内で
ajaxなどを使用してページ遷移(のような動き)を実装しているものを指します。
また、Vue.jsを使用して実装することを想定しています。
※随時更新していきます。

FAQ

Google Analyticsの測定はできるの?

可能です。
vue-routerでURLを切り替える際に、手動でGA側にページ遷移を通知することで測定されます。
(ブラウザバックではページカウントが増えません)

任意のイベントを通知することも可能ですが、懸念点としては下記が挙げられます(未検証)

  • GAタグ以外で、手動でページ遷移を通知する方法がない計測タグは対応できない
  • Google Tag Managerを使ってページごとに任意の処理を行いたい場合、対応できない可能性がある

参考

https://developers.google.com/analytics/devguides/collection/gtagjs/single-page-applications
http://sys1yagi.hatenablog.com/entry/2016/11/30/233414

Googleにちゃんとindexされるの?

sitemap.xmlを設置してSearch Consoleで登録すればindexされます。
また、indexされたページもtitleやmeta情報などが反映されており、
Google側でjsが実行された状態でindexされます。

sitemap.xmlの自動生成ツールは使えるの?

使えません。
sitemap.xmlを自動生成するツールは多数Web上にありますが、
SPAに対応してるものはありませんでした。

例:
http://www.sitemapxml.jp

なので、SPAの場合においては、sitemap.xmlを手動作成する必要があります。

ページのURLを自由にカスタマイズできるの?

できます。
Vue.jsがもつvue-routerというモジュールがあり、ページURLと使用するモジュールを紐付けることができます。

URLに動的なパラメータを持たせることも可能です。
下記のページで商品URLの例を確認することができます
ただし、対応する場合はサーバー側でリライトモジュールの設定が必要です。

任意のパラメータを付与させることはできるの?

できます。
vue-routerではパラメータを下記の記載で取得可能です

code
{{ $route.query.name}}

参考:動的ルートマッチング
https://router.vuejs.org/ja/guide/essentials/dynamic-matching.html#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E5%A4%89%E6%9B%B4%E3%81%AE%E6%A4%9C%E7%9F%A5

効果測定タグをページ毎に出し分けできるの?

できます。
ただし、ページ遷移をした時点でHTMLの評価は行われるようなので、
Vue.jsのif構文で表示制御をする必要があります。
コンバージョンなどについては当記事内の
「ブラウザバックで戻ったときにコンバージョンタグが複数回カウントされたりしないの?」
の回答を参考のこと

title/metaをページ毎にカスタマイズできるの?

Vue.jsのプラグインである「Vue-Meta」を利用することで実現できます。

Vue-Meta
https://github.com/declandewet/vue-meta#recognized-metainfo-properties

このプラグインでは下記のことが可能です。

  • titleをページ毎に書き換える
  • metaタグ(keywords、description、OGPタグなど)をページ毎に設定する
  • 任意のCSSファイル・javascriptファイルを読み込む
  • nosriptタグを埋める
  • html/bodyに属性を指定する

ブラウザバックで戻ったときにコンバージョンタグが複数回カウントされたりしないの?

実装で回避することができます。
以下のような処理順で行うことで、重複カウントは防げます。
(下記は処理完了ページの例)

※処理完了ページの例
1)ページアクセス
2)APIにてセッションチェック
3)2がOKであれば、完了ページ表示フラグを立てる
4)コンバージョン用のHTMLが出力される

ブラウザバックでアクセスされた場合、2のチェックでエラーになるため、
3以降が行われることなく、エラーページが表示されます。
Vue.js内にHTMLとして普通に記載していると、セッションチェックが終わる前にHTMLを評価してしまうので、
出力フラグを用意し、そのフラグによって出力制御をすることで実現可能です。

各ページのアクセスログって残るの?

残りません。
Single Page Applicationはすべてクライアント側でHTMLの描画を行うので、サーバー側にログは残りません。
ただし、APIを呼び出したログは残ります。
SPA側で「ページ切り替えを行った」ということは検知できるので、その際にログ記録用のAPIに
URL情報を付与して送るなどしておけば、アクセスログを取ることは可能です。

例)
https://~.co.jp/api/accesslog.asp?url=/pd/cosme/test001

ログ記録用APIをわざわざ呼ぶと、APIコール1回分がコストになるので、セッション確認APIに
付与するようにするなどすればより良いです。

そもそもSPAって早いの?

処理自体は変わっておらず、その処理をクライアント側で行うか、サーバー側で
行うかだけの違いなので、処理速度はクライアント側のPCスペックや通信環境に依存します。
SPAでは初回ロード時に必要なjavascriptファイル・CSSファイルはダウンロードされるため、
2ページ目以降の表示は早くなります。また、ヘッダー・フッターなど
共通の部分は最初のページのみでロードされるので、2ページ目以降は少し早くなります。

ただし、SPAではHTMLの構築をすべてjavascriptで行い、ページ遷移がないため、
メモリリークが発生するリスクがあります。
(構築されたHTML情報はメモリに展開されるため)
そのため、場合によっては任意のページでリフレッシュする方が良い場合もあります。
これは状況によるのでサイト毎に考えてください。

SPAで多言語対応ってどうやるの?

Vue.jsのi18nプラグインで対応可能です。
https://qiita.com/_upto_me_/items/6d76dcd2d2c09b1bcb88

手順は以下の通り。

1)i18nプラグインをインストール
2)Vue.jsインスタンス生成時に多言語設定を追加
3)初期値のlocaleを設定
4)JSON形式のmessageファイルを作成
5)HTML内にmessage埋め込み
6)言語切替処理を実装

messageファイルは下記のような形式で作ります。

message.json
{
    "ja": {
      "message": {
        "title": "タイトル",
        "dashbord": "ダッシュボード",
        "subtitle": "ここがサブタイトルになります"
      }
    },
    "en": {
      "message": {
        "title": "title",
        "dashbord": "Dashboard",
        "subtitle": "sub title"
      }
    },
    "han": {
      "message": {
        "title": "標題",
        "dashbord": "儀表盤",
        "subtitle": "副標題"
      }
    }
  }

言語毎にjsonファイルを分けることも可能です。
また、HTML内の記載はこうなります。

html
<h1>{{$t("message.dashbord")}}</h1>

手順の3で設定したlocaleを「jp」や「en」に変更することで、
message.jsonに定義された言語に変わります。
{{$t("message.dashbord")}}の形式で記載されてない箇所は変わりません。

サンプル
http://spa-sample.ivp.co.jp/entry1
(ページ最上部の「ダッシュボード」が変わります)

ページスクロールした位置って記憶してるの?

記憶してます。下記のような動作になります。

1)検索結果ページでページ下部までページスクロール
2)別ページへ遷移
3)ブラウザバックで戻る
4)ページ下部で表示される

また、ブラウザバックではなく、リンクで戻った場合はページ最上部に行きます。

1)検索結果ページでページ下部までページスクロール
2)別ページへ遷移
3)リンクで商品一覧に戻る
4)ページ最上部で表示される

セキュリティでCSRF対策はどのようにすればよいか?

基本的にはサーバーサイドで制御するときと同じです。

1)入力確認画面でトークンを発行する
2)完了画面で1で発行したトークンが生きてることを確認する
3)2がOKであれば登録処理を行い、1のトークンを無効化する

この内容で完了画面への直接アクセス(CSRF)は防げます。
確認画面でのトークン発行は「登録完了」Buttonを押下したタイミングで発行します。
なので、確認→完了のあたりの処理順は下記のようになります。

1)確認画面表示
2)完了画面へ進むボタンクリック
3)トークン発行APIをコール(この時点ではURLは確認画面のまま)
4)完了画面へ遷移
5)登録APIをコール
6)3のトークンが無効化
7)直接完了画面にアクセスしてもトークンがないのでエラーになる

参考情報として、
Laravel(PHPフレームワーク)では、サーバ側でCSRFトークンを発行する形で対応しているようです。
http://blog.asial.co.jp/1496

sitemap.xmlは設置できるの?

設置可能です。

ただし、vue-cliを使ってドメイン直下にファイルを置く場合には、
vue-routerでのルーティング設定か、webpackの設定調整が必要です。
上記サイトではwebpackの設定を調整したので、
staticフォルダ以下に配置しています。

拡張子の制限はないの?

ありません。

faviconを設置したいんだけどこに置けばいいの?

sitemap.xmlや画像フォルダと同じ場所におけばOKです。

httpステータスコードは制御できるの?

できません。
SPAの場合は大本になってるファイルがあり、その中で仮想的にHTMLを構築するため、
存在しないページURLでアクセスされても、200が返却されます。
200が返却された後、ページ表示処理で404のような表示をすることは可能ですが、
httpステータスコード自体を404にすることは不可能です。

ファイル更新したときにキャッシュはどう処理されるの?

色んなアプローチがあると思いますが、Vue.jsではWebPackという技術を使用することで回避しています。
Vue.jsには、vue-cliという開発するためのツールや設定がまとまったものがあるのですが、
その中にwebpackの設定が同梱されています。

vue-cli
https://jp.vuejs.org/2015/12/28/vue-cli/

vue-cliに含まれてるwebpackの設定では、複数のjsファイルやcssファイルを
結合して出力する際、ソースの内容からハッシュ値を生成しファイル名に付与します。

たとえば、

adbeebb4-c790-9c75-6a9b-93d750adb3f2.png

この状態からvue.jsで管理されているhtmlに修正をいれてbuildしなおすと

てst.png

こうなります。
変更が入っていないファイルはそのまま、で変更が入っているファイルはハッシュ値が変わっています。
これでキャッシュされることはありません。

ただし、これはwebpackで管理されているファイルだけなので、webpackの管理外に
なっているファイルは、ハッシュ値の付与がされないので、キャッシュの問題が発生します。
完全に回避しようとすると全てをwebpack管理する必要がありますが、
ルーティングに関するjsファイルは標準でwebpack管理されているので、
ページ遷移できないなどのリスクは少ないのでは・・・・と考えています。

その他

質問などあればコメントに書いていただければと思います!
判明したものから随時アップデートしていきます!