Laravel 5.4 と Vue.js 2.2 と JWTAuth で、ログインできる SPA なTODOアプリを作ってみました。
結構本気で作りました。
Laravel + Vue.js はかなり作りやすく、サーバーサイドの処理が少ない&分かりやすいので楽しかったです。
自分の記憶の定着を上げるため、チュートリアルを書きます。
JS初心者なので、おかしいところとかベストじゃないところが多々あると思いますので
ご指摘頂けたら嬉しいです!
作ったもの
ソースコード
デモサイト
目次
四部作です。
- はじめに ← 今ここ
- 基本的な概念の説明
- インストール
-
Todoアプリ作成編 / サーバーサイド
- Model作成
- Restfulコントローラー
- curlコマンドでテスト
- Model Factory と Seeding
- ユニットテストを書く
-
Todoアプリ作成編 / フロントエンド
- Vue インスタンスを作る
- Vue component とルーティング
- axios を使ってリクエストを飛ばす
-
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 Factory
やFaker
,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が破棄される
- 次回以降 Request Header に
[追記]
参考にしたプロジェクトではローカルストレージにTokenを保存していましたが、クッキーの方がセキュリティ上良いようです。
[/追記]
ちなみに、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
を使います。
# 初期設定で MySQL を使うような設定になっているが、 SQLite を使うため DB_ から始まる設定を削除
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
付けないと動かなかった。この辺は環境に依りそう。
{
...,
"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
{
...,
"scripts": {
"dev": "cross-env.js ...",
"watch": "cross-env.js ...",
"hot": "cross-env.js ...",
"production": "cross-env.js ...",
},
...
}
※さらに追記
@kimama1997 さんに、現在Gitリポジトリの方では修正されていることを教えて頂きました。
こちらの Mar 6, 2017 のコミットで修正されているので、そのうち修正されるはずです。
Laravelのディレクトリ構成
一旦整理しておくと、
- フロントエンドは、
resources/
- サーバーサイドは、
app/
- ルーティングは 、
routes/
に置いていくことになります。
.
|-- package.json
|-- resources
| |-- views
| | `-- app.blade.php
| `-- assets
| `-- js
| `-- app.js
`-- routes
`-- web.php
Hello World
Laravel はアプリケーション開始時に単一Viewを返すだけで良いので、ルーティングは至極単純です。
ブラウザのルーティングはVue.jsが担当します。
<?php
Route::get('/{any}', function () {
return view('app');
})->where('any', '.*');
SPAの起点となるViewを作ります。
<div id="app"> ... </div>
の部分が Vue によって操作されます。
<!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>
require('./bootstrap');
Vue.component('example', require('./components/Example.vue'));
const app = new Vue({
el: '#app'
});
起動
yarn run dev
php artisan serve
localhost:8000 で起動してるはず。
Vue.jsが表示できました。
このまま開発するなら、 yarn run watch
を起動しておきましょう。変更を検知してJSをビルドしてくれます。
とりあえずここまで。