はじめに
Laravel + Vue.js + TypeScriptなアプリケーションの開発方法を書きます。
Laravel はサーバサイドの Web アプリケーションフレームワークなので、Vue.js とか TypeScript とか関係ないんじゃないのと思われるかもしれませんが、Laravel には Laravel Mix という Webpack のラッパーライブラリが付属しており、設定不要なフロントエンド開発が可能となっています。
前提
Tool | Ver. |
---|---|
Laravel | 5.5 |
Vue.js | 2.5 |
TypeScript | 2.6 |
Node.js | 8.9.1 |
npm | 5.5.1 |
homestead | 7.0.1 |
homestead (Vagrant Box) | 5.0.1 |
ちなみに開発機はMacです。
まずはプロジェクトの新規作成
$ cd ~/code
$ composer create-project laravel/laravel --prefer-dist sample_app
$ cd sample_app
$ yarn install
開発環境の設定も行ないます。
folders:
- map: ~/path/to/your/homestead
to: /home/vagrant/code
sites:
- map: homestead.test
to: /home/vagrant/code/sample_app/public
192.168.10.10 homestead.test
ここまでで、http://homestead.test でウェルカム画面が見られるはずです。
npmパッケージの追加インストール
$ yarn add -D ts-loader typescript vue-property-decorator
tsconfig.jsonを追加
{
"compilerOptions": {
"outDir": "./built/",
"sourceMap": true,
"strict": true,
"noImplicitReturns": true,
"noImplicitAny": true,
"module": "es2015",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node",
"target": "es5",
"lib": [
"es2016",
"dom"
]
},
"include": [
"resources/assets/ts/**/*"
]
}
プロジェクトルート(.env
があるディレクトリ)に上記tsconfig.json
ファイルを追加します。
TypeScriptの環境設定は以下のサイトを参考にしました。
- John Papa: Vue.js with TypeScript
- Microsoft/TypeScript-Vue-Starter: A starter template for TypeScript and Vue with a detailed README describing how to use the two together.
vue.shims.d.ts
再び上のサイトを参考に、resources/assets/ts
配下に下記の型定義ファイルを追加します。単一ファイルコンポーネント(*.vue
ファイル)についてのコード解析を助けるものらしいです。
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
webpack.mix.js を編集
let mix = require('laravel-mix');
mix.ts('resources/assets/ts/app.ts', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css');
デフォルトだと mix.js(...)
となっているところを ts
メソッドを使うように変更します。
TypeScriptコードの格納ディレクトリも asset/js
から asset/ts
に変更しています。
Let's Coding !
設定はこれで終了!超簡単ですね
あとはコードを書いていくだけですが、簡単なサンプルを載せてみます。
テンプレートファイル
まずはテンプレートです。既存の welcome.blade.php
ファイルを編集しました。
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Laravel</title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<main>
<div id="app"></div>
</main>
<script src="/js/app.js"></script>
</body>
</html>
エントリポイント
次にエントリポイントとなる app.ts
ファイルです。デフォルトでついてる bootstrap.js
も(Axiosの設定だけ)活かしてみました。JavaScriptだと require('./bootstrap.js')
で bootstrap.js
の内容を実行しているのですが、TypeScriptではそういうことはできないようなので、関数にしました。
import Vue from 'vue';
import bootstrap from './bootstrap';
import AppComponent from './components/App.vue';
bootstrap();
const app = new Vue({
el: '#app',
render: h => h(AppComponent)
});
import Axios, { AxiosStatic } from 'axios';
declare global {
interface Window {
axios: AxiosStatic;
}
interface Element {
content: string;
}
}
export default function bootstrap() {
window.axios = Axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
}
コンポーネント
続いてコンポーネントです。単一ファイルコンポーネントでの記述が可能です。
ポイントはまず、<script>
タグに lang="ts"
を指定してあげることです。
また vue-property-decoratorを利用していますので Component
や Prop
といったデコレータを使ってクラスベースでコンポーネントを表現していきます。
なんだか見た目がAngularっぽいですね(??)
上記 bootstrap.ts
で設定したAxiosが使えるか確かめたかったので、ダミーデータAPIにリクエストしています。
<template>
<div class="App">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1>{{ message }}</h1>
<p class="text-center">
<button class="btn btn-primary" @click="load">Load Users</button>
</p>
<users :users="users"></users>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import UsersComponent from './Users.vue';
import User from '../models/User';
@Component({
components: {
'users': UsersComponent
}
})
export default class App extends Vue {
message = 'Laravel + Vue.js + TypeScript';
users: User[] = [];
load() {
// 下記サイトからダミーデータ取得
// https://jsonplaceholder.typicode.com/
window.axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
this.users = response.data;
})
.catch(err => console.error(err));
}
}
</script>
<style scoped>
h1 {
text-align: center;
margin: 4rem 0;
}
</style>
下記の通り、<style>
で lang
の指定をするとSassも使用できます。
<template>
<div class="Users">
<div class="list-group">
<div class="list-group-item" v-for="user in users">
<h4>{{ user.name }} <small>@{{ user.username }}</small></h4>
<p>{{ user.email }}</p>
<p>{{ user.phone }}</p>
<address>
{{ user.address.street }}
{{ user.address.suite }}
{{ user.address.city }}
{{ user.address.zipcode }}
</address>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator';
import User from '../models/User';
@Component
export default class Users extends Vue {
@Prop() users: User[];
}
</script>
<style lang="scss" scoped>
.list-group-item {
padding: 2rem;
h4 {
margin-top: 0;
}
address {
margin-bottom: 0;
}
}
</style>
ダミーのユーザーデータを取得するので、そのレスポンス形式に合わせてUser型を作成(TypeScriptっぽいことがしてみたかった)。
export default interface User {
id: number;
name: string;
username: string;
email: string;
address: Address;
phone: string;
website: string;
company: Company;
}
export interface Address {
street: string;
suite: string;
city: string;
zipcode: string;
geo: {
lat: string;
lng: string;
}
}
export interface Company {
name: string;
catchPhrase: string;
bs: string;
}
ビルド
$ yarn run dev
ビルドが成功すると以下の画面が表示されるはず。
ボタンを押すと、ダミーユーザーたちのデータを取得できています。
まとめ
以上、Laravel Mix を利用して Vue.js + TypeScript なアプリケーションの開発を行う方法でした。
最近では Poi や parcel など、なるべく簡単にフロントエンドの設定を行うビルドツールが求められているようですが、Laravel Mixもなかなか強力なツールなのではないでしょうか。
Laravel Mix は Laravel 本体とは独立した npm パッケージですので、Laravel は使わなくとも、フロントエンドのビルドツールとして Laravel Mix のみ利用するというのもアリかもしれません。