Help us understand the problem. What is going on with this article?

Laravel 5.4 と Vue.js 2.2 と JWTAuth で、ログインできる SPA アプリケーションのチュートリアル 1/4

Laravel 5.4 と Vue.js 2.2 と JWTAuth で、ログインできる SPA なTODOアプリを作ってみました。

結構本気で作りました。
Laravel + Vue.js はかなり作りやすく、サーバーサイドの処理が少ない&分かりやすいので楽しかったです。

自分の記憶の定着を上げるため、チュートリアルを書きます。

JS初心者なので、おかしいところとかベストじゃないところが多々あると思いますので
ご指摘頂けたら嬉しいです!

作ったもの

ソースコード

https://github.com/acro5piano/laravel-vue-jwtauth-spa-todo-app

デモサイト

https://laravel-vue-jwtauth.herokuapp.com/

1dfccb22-fdce-179d-8e85-f52a502662c2.png

目次

四部作です。

  1. はじめに ← 今ここ
    • 基本的な概念の説明
    • インストール
  2. Todoアプリ作成編 / サーバーサイド
    • Model作成
    • Restfulコントローラー
    • curlコマンドでテスト
    • Model Factory と Seeding
    • ユニットテストを書く
  3. Todoアプリ作成編 / フロントエンド
    • Vue インスタンスを作る
    • Vue component とルーティング
    • axios を使ってリクエストを飛ばす
  4. JWTAuthでログイン編
    • JWTAuth インストール
    • ログインとaxiosにヘッダー追加
    • Storeパターンを使ってログインState維持
    • ログアウト

一応部分的に読めるようにしていて、

  • 概念だけ知りたい方は 1
  • Vue.jsをちょっと使いたい方は 3
  • JWTAuthの実装について知りたい方は 4

だけで良いのかなと思います。

著者のスペック

  • 本職はサーバーサイドエンジニア
  • Rails, Laravel の使用経験があり、MVC, DBマイグレーション, RESTFul, ActiveRecord などの概念は一通り分かる
  • JSはjQueryで止まってる
    • ES6なにそれ
    • React, Redux, Angular, Ember どれも使用経験無し
  • Vue.jsの公式ガイドは一通り読んで、データバインディングについては理解した

参考にしたサイト

  • Koel 個人の音楽ストリーミングサービス。Laravel + Vue.js + JWTAuth で作られたSPAアプリケーション。一番 パクった 参考にした。
  • Vuedo Laravel + Vue.js のブログプラットフォーム。
  • Laravel.io / Laracast / Stack Overflow での無数の Q&A

それでは始めます。

基本的な概念

実際に手を動かす前に、今回やりたいこと、学んだことを書いていきます。

なぜ Single Page Application (SPA) にするのか

ユーザー体験が良いためです。

今回作って思いましたが、最初の読み込みさえ終わってしまえばサクサク動くSPAは気持ちいいですね。

モバイルとの相性も良いです。

なぜ Laravel を使うのか

Laravelいいっすね。

  • フロントエンドの処理を NodeJS (5.4では Webpack )に任せているのでシンプル。
  • 最初から Vue.js、 Webpack によるコンポーネント志向のアプリケーションが作れる
  • 軽くて、依存関係などのトラブルが少ない(主観)
  • Model FactoryFaker, PHPUnit など、開発支援がPHP随一で強い

Railsはフロントが難しい印象があります。 5.1 で変わったのかな・・・

なぜ Vue.js を使うのか

Laravelと相性が良いためです。

Laravelを作った人がVue.jsを支援しているので、
Laravelをインストールすると、既にVue.jsもインストールされてます。

Vue.js はモダンなJavaScriptのフレームワークです。

アプリケーション全体の中で部分的に導入することもできますが、
豊富なエコシステムによりシンプルで軽量なSPAを構築することができます。

今回使ったのは下記です。

  • vue-router
  • vue-spinner
  • axios

もっと大きなアプリケーションになれば、 vuex のような状態管理方法を使うと良いと思います。

JWTAuth とは

シンプルな認証方式です。

Introduction to JSON Web Tokens を読めば仕様は何となく理解できると思います。

かいつまんで説明すると

  • クライアント: ユーザー名とパスワードをサーバーに送信
  • サーバー: ログインを試行し、成功したら Json Web Token を返す
  • クライアント: Token を受取り、
    • 次回以降 Request Header に Authentication を追加 ( Authentication: Bearer "yowqeh43gb093fh023.....orhgoerg=" )
    • ローカルストレージ(かクッキー)に Token を保存し、Token の有効期限が切れるまでログイン状態維持
    • ログアウト時は明示的にTokenが破棄される

[追記]

参考にしたプロジェクトではローカルストレージにTokenを保存していましたが、クッキーの方がセキュリティ上良いようです。

https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

[/追記]

ちなみに、Laravel5.3からは Passport という機能が追加されており、
それを使えばAPIサーバーが簡単に構築できるらしいです。

ですが、下記のQ&Aによると

OAuth or JWT? Which one to use and why?
http://stackoverflow.com/questions/32964774/oauth-or-jwt-which-one-to-use-and-why

If you want simple stateless http authentication to an api,
then JWT is just fine and relatively quick to implement, even for a novice developer.

とのことなので、JWTAuthを使いました。

Passportは OAuth を利用しており、APIを外部に公開するならばぜひ使いたい機能ではあります。

コンポーネント

Vue.js は React と同様、コンポーネント志向です。

例えば下記のような拡張子が .vue のファイルを作り、それを Webpack で結合してブラウザに渡します。

<template>
  <h1>Hello, {{ name }}</h1>
</template>

<script>
  export default {
    data () {
      return {
        name: 'Kazuya'
      }
    },
  }
</script>

<style>
  .h1 {
    border-bottom: 1px;
  }
</style>

  <h1>Hello, Kazuya</h1>

jQuery 脳から進歩していない私にとって奇妙でしたが、すぐに慣れました。

このようなコンポーネントを組み合わせて、SPAを作っていきます。

インストール

PHP

いつもと同じ、 Laravel のインストール手順です。

composer create-project --prefer-dist laravel/laravel spa-todo 5.4
cd spa-todo

Database

デモなので sqlite を使います。

perl -i -pe 's/DB_.+\n//g' .env
echo 'DB_CONNECTION=sqlite' >> .env
touch database/database.sqlite

# DBのマイグレーションして動くことを確認
php artisan migrate

# => Migrated: 2014_10_12_000000_create_users_table
# => Migrated: 2014_10_12_100000_create_password_resets_table

NodeJS

SPAに必要なモジュールを追加します。

関係ないですが、 yarn 速くて気持ちいいですね。

# お好みでyarn使おう
npm install -g yarn

# vue-router 追加
# npm install --save-dev vue-router でも一緒
yarn add vue-router vue-spinner --dev

package.json 修正

bin/ の前に dist 付けないと動かなかった。この辺は環境に依りそう。

package.json
{
  ...,
  "scripts": {
    "dev": "node_modules/cross-env/dist/bin/cross-env.js ...",
    "watch": "node_modules/cross-env/dist/bin/cross-env.js ...",
    "hot": "node_modules/cross-env/dist/bin/cross-env.js ...",
    "production": "node_modules/cross-env/dist/bin/cross-env.js ...",
  },
  ...
}

※ 追記

@notice_inc さんに教えて頂き、下記のように修正した方がパスがすっきりしていいようです。
ありがとうございました。

yarn add cross-env --dev
package.json
{
  ...,
  "scripts": {
    "dev": "cross-env.js ...",
    "watch": "cross-env.js ...",
    "hot": "cross-env.js ...",
    "production": "cross-env.js ...",
  },
  ...
}

※さらに追記

@kimama1997 さんに、現在Gitリポジトリの方では修正されていることを教えて頂きました。
こちらの Mar 6, 2017 のコミットで修正されているので、そのうち修正されるはずです。

https://github.com/laravel/laravel/commit/65c4d16d04f030633b0b4a09c2c98f716e5c1873#diff-b9cfc7f2cdf78a7f4b91a753d10865a2

Laravelのディレクトリ構成

一旦整理しておくと、

  • フロントエンドは、 resources/
  • サーバーサイドは、 app/
  • ルーティングは 、 routes/

に置いていくことになります。

.
|-- package.json
|-- resources
|   |-- views
|   |   `-- app.blade.php
|   `-- assets
|       `-- js
|           `-- app.js
`-- routes
    `-- web.php

Hello World

Laravel はアプリケーション開始時に単一Viewを返すだけで良いので、ルーティングは至極単純です。

ブラウザのルーティングはVue.jsが担当します。

routes/web.php
<?php
Route::get('/{any}', function () {
    return view('app');
})->where('any', '.*');

SPAの起点となるViewを作ります。

<div id="app"> ... </div> の部分が Vue によって操作されます。

resources/views/app.blade.php
<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Vue TODO</title>

        <link rel="stylesheet" href="css/app.css">

        <script>
            window.Laravel = {};
            window.Laravel.csrfToken = "{{ csrf_token() }}";
        </script>
    </head>
    <body>
        <div id="app">
          <example></example>
        </div>
    </body>

    <script src="js/app.js"></script>
</html>
resources/assets/js/app.js
require('./bootstrap');

Vue.component('example', require('./components/Example.vue'));

const app = new Vue({
    el: '#app'
});

起動

yarn run dev
php artisan serve

localhost:8000 で起動してるはず。

localhost-8000-(iPhone 6).png

Vue.jsが表示できました。
このまま開発するなら、 yarn run watch を起動しておきましょう。変更を検知してJSをビルドしてくれます。

とりあえずここまで。

acro5piano
React.js / TypeScript / DevOps エンジニア。 Fastlane のコントリビューター。 Terraform と Ansible のファン。 https://twitter.com/acro5piano
https://github.com/acro5piano
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした