実行環境
・osバージョン
rocky linux 8.7
・composerパッケージのバージョン
laravel 9.48.0
inertiajs/inertia-laravel v0.5.4
laravel-lang/lang 12.11.5
laravel-lang/publisher v14.5.0
・node(npm)パッケージで関連しそうなバージョン
typescript@4.9.4
@inertiajs/inertia-vue3@0.6.0
@inertiajs/inertia@0.11.0
@inertiajs/progress@0.2.7
@vue/compiler-sfc@3.2.45
laravel-mix-vue3@0.7.0
vue-i18n@9.2.2
vue-loader@17.0.1
vue-template-compiler@2.7.14
vue@3.2.45
各ファイルの設定
webpack.mix.js
const mix = require('laravel-mix');
require("laravel-mix-vue3");
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel applications. By default, we are compiling the CSS
| file for the application as well as bundling up all the JS files.
|
*/
mix.ts('resources/js/app.ts', 'public/js')
.postCss('resources/css/tailwind.css', 'public/css', [require('tailwindcss'), require('autoprefixer')])
.sass('resources/scss/bootstrap.scss', 'public/css')
.alias({
'@': 'resources/js',
})
.babelConfig({
plugins: ['@babel/plugin-syntax-dynamic-import'],
})
.webpackConfig({
stats: {
children: true
}
})
.sourceMaps()
.vue();
if (mix.inProduction()) {
mix.version();
}
tsconfig.json
有効な設定の未抜粋。
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"baseUrl": "./",
"typeRoots": [
"./resources/js/@types"
],
"types": [
"node"
],
"resolveJsonModule": true,
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": false,
"skipLibCheck": true
},
"include": [
"./resources/js/**/*"
]
}
package.json
scriptsのみ抜粋
{
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
},
}
多言語化(i18n)
laravel側の設定
$ composer require laravel-lang/lang laravel-lang/lang-publisher --dev
$ composer require laravel-lang/attributes laravel-lang/http-statuses --dev
$ php artisan lang:add en ja
laravel9からlang
はプロジェクトの直下に置かれる。
./lang
+ en
+- auth.php
+- http-statuses.php
+- pagination.php
+- passwords.php
+- validation.php
+ ja
+- auth.php
+- http-statuses.php
+- pagination.php
+- passwords.php
+- validation.php
デフォルトでないものは各言語ファイルに追加する。
vue3のフォルダ構成
resources
配下にjs
,css
,scss
,views
を用意。
resources/scssとresources/css
前述のwebpack.mix.js
で指定したように
tailwindとbootstrapで別々のcssファイルに生成するようにする。
scss
にはbootstrap.scssファイルを格納
css
にはtailwind.cssファイルを格納、
さらにアプリ独自のcssファイルの設定があれば
app.cssなどを用意する。
resources/scss/bootstrap.scss
@import "~bootstrap/scss/bootstrap";
resources/css/tailwind.css
@tailwind base;
@tailwind components;
@tailwind utilities;
resources/views
既存のテンプレートシステム(*.blade.php
)が格納されているが
vue3ではjs配下に画面+処理のファイル(*.vue
)を格納するので
app.blade.php
のみ使用する。
resources/views/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title inertia>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
<!-- Styles -->
<!-- <link rel="stylesheet" href="{{ asset('css/app.css') }}"> -->
<link rel="stylesheet" href="{{ asset('css/tailwind.css') }}">
<!-- Scripts -->
@routes
<script src="{{ mix('js/app.js') }}" defer></script>
@inertiaHead
</head>
<body class="font-sans antialiased">
@inertia
@env ('local')
<!-- <script src="http://localhost:8080/js/bundle.js"></script> -->
@endenv
</body>
</html>
@routes
はvueファイルのscript内やtemplate内でlaravelのroute
関数を使えるようにする指定
@env('local')
はローカル環境で動作させるとき
内部のサーバ経由にするらしいが余計なお世話なのでコメント化。
resource/js
resource/js/bootstrap.js
larave-echo
とpusher-js
はブロードキャスト機能の記述のため使わない場合はコメント化
window._ = require('lodash');
try {
require('bootstrap');
} catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
// import Pusher from 'pusher-js';
window.Echo = new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
cluster: process.env.MIX_PUSHER_APP_CLUSTER,
forceTLS: true,
wsHost: 'websocketのホスト名',
wssPort: 443,
});
resource/js/app.ts
laravelの言語ファイル(php/json)をvue3で使うための設定も追記。
route
にziggy-js
を使う。
import './bootstrap';
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import { createI18n } from 'vue-i18n';
import route from 'ziggy-js';
import 'flowbite';
import * as jaJP from '../../lang/ja.json';
import * as enUS from '../../lang/en.json';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || '';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}`),
setup({ el, app, props, plugin }) {
const setupAsync = async ({ el, app, props, plugin }) => {
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: 'ja-JP',
fallbackLocale: 'en-US',
messages: {
'en-US': enUS,
'ja-JP': jaJP
}
});
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(i18n)
.mixin({ methods: { route } })
.mount(el);
}
setupAsync({ el, app, props, plugin });
},
});
InertiaProgress.init({ color: '#4B5563' });
さらに以下のフォルダを配置する。
resource/js/@types
typescript用の型定義ファイルを置く。コンパイルやvscodeでのエラー対策。
tsconfig.jsonにて指定する。
declare module '*.vue' {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
interface Window {
Echo: any;
Pusher: any;
}
declare var window: Window;
resource/js/Components
画面の各ボタンやロゴやInputなどの部品を格納初期である程度用意されている。
ApplicationLogo.vue
,Checkbox.vue
,DropdownLink.vue
,InputError.vue
,NavLink.vue
,ResponsiveNavLink.vue
,Button.vue
,Dropdown.vue
,Input.vue
,Label.vue
,NavMenu.vue
,ValidationErrors.vue
resource/js/Layouts
ログインしていない状)用のレイアウトファイル(Guest.vue)とログイン済みユーザー用のレイアウトファイル(Authenticated.vue)
ヘッダー要素と各ページへの指定がされているだけ。
resource/js/Pages
各ページの内容を記載する。
初期はログイン前の画面(Welcome.vue
)とログイン後の画面(Dashboard.vue
)と
Pages/Auth
に認証に必要な画面(ログイン、アカウント作成、パスワードリセット)のみ。
{{ $t('Dashboard') }}
の個所は後述の多言語化対応
<script setup lang="ts">
import BreezeAuthenticatedLayout from '@/Layouts/Authenticated.vue';
import { Head, Link } from '@inertiajs/inertia-vue3';
</script>
<template>
<Head>
<title>{{ $t('Dashboard') }}</title>
<meta name="description" content="Dahsboad page description">
<link rel="shortcut icon" href="/favicon.svg">
</Head>
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ $t('Dashboard') }}
</h2>
</template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
{{ $t("You are logged in!") }}
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
vueファイル内で多言語メッセージを使う場合は{{ $t('メッセージ') }}
のように使用する。