Vuetify3.0がなかなかリリースされないので他のUIも使ってみたけどやっぱりVuetify!もうすぐリリースされるらしいけどBeta版を試してみる。Laravel9/Vite/Vue3でインストール&設定をしてみましょう。
目次
開発環境
Windows10
Docker Desktop
WSL2
Laravel9
Laravel Sail
Vue3.2.40
Vuetify3(Beta)他のインストール
$ sail npm install vuetify@next
$ sail npm install vite-plugin-vuetify
$ sail npm install -D sass
Viteの設定
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue'
import vuetify from "vite-plugin-vuetify" // 追加
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
vue(),
vuetify(), // 追加
],
server: {
hmr: {
host: 'localhost',
},
},
});
app.jsでの組み込み設定
import './bootstrap';
import VueScrollTo from 'vue-scrollto'
import { createApp } from 'vue'
import 'vuetify/lib/styles/main.sass' // 追加
import { createVuetify } from 'vuetify' // 追加
import App from './components/App.vue' // 追加
const vuetify = createVuetify()
const app = createApp(App)
app.use(vuetify) // 追加
app.mount("#app")
Viteプラグイン「vite-plugin-vuetify」は使用しているコンポーネントを自動でimportしてくれるので下記のような記述は不要。
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
const vuetify = createVuetify({
components,
directives,
})
アイコンフォント(Material Design Icon)
①デフォルトのアイコンフォント @mdi/fontを使用する場合
mdi/fontのインストール
$ sail npm install -D @mdi/font
app.js
import '@mdi/font/css/materialdesignicons.css' // 追記
Vueファイル
<div>
<v-icon icon="mdi-account" class="ma-4" />
<v-btn
icon="mdi-account"
color="success"
class="ma-4"
></v-btn>
</div>
②mdi/jsパッケージを使用する場合
必要なアイコンのみカスタムインポートすることでバンドルサイズを小さくすることができる。
mdi/jsのインストール
$ sail npm install -D @mdi/js
app.js
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg' // 追記
import '@mdi/font/css/materialdesignicons.css' // 追記
// 変更
const vuetify = createVuetify({
icons: {
defaultSet: 'mdi',
aliases,
sets: {
mdi,
},
},
})
Vueファイル
<div>
<v-icon :icon="mdiAccount" class="ma-4" />
<v-btn
:icon="mdiAccount"
color="success"
class="ma-4"
></v-btn>
</div>
<script setup>
import { mdiAccount } from '@mdi/js'
</script>
iconはv-bindディレクティブ(:icon)になっていることに注意!
サンプル作成
・Vuetify3のv-app-barとv-navigation-drawerを使用してドロワーメニューの画面レイアウトを作成。
・HomeページにはVuetify3でButton、Icon、Alert、Cardを配置。
・Vue-Router4で画面遷移。
・AboutページではscrollBehaviorとvue-scrolltoでスクロール制御。
Route::get('/app/{any?}', function() {
return view('app');
})->where('any', '.*');
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue'
import vuetify from "vite-plugin-vuetify"
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
],
refresh: true,
}),
vue(),
vuetify(),
],
server: {
hmr: {
host: 'localhost',
},
},
});
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<div id="app"></div>
</body>
</html>
import './bootstrap';
import router from './router'
import VueScrollTo from 'vue-scrollto'
import 'vuetify/lib/styles/main.sass'
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
import App from './components/App.vue'
import '@mdi/font/css/materialdesignicons.css'
const vuetify = createVuetify()
const app = createApp(App)
app.use(router)
app.use(VueScrollTo, {
offset: -60,
})
app.use(vuetify)
app.mount("#app")
import { createRouter, createWebHistory, useRouter } from 'vue-router'
import VueScrollTo from 'vue-scrollto'
const BASE_URL = '/app'
import Home from './components/Home.vue'
import About from './components/About.vue'
import NotFound from './components/404.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
{
path: '/404',
name: '404-NotFound',
component: NotFound,
},
{
path: '/:pathMatch(.*)',
redirect: '/404',
},
]
const router = createRouter({
history: createWebHistory(BASE_URL), // set BASE_URL
routes,
// scrollBehavior(to, from, savedPosition) {
// if (to.hash) {
// return { el: to.hash, behavior: 'smooth', top: 60 }
// } else if (savedPosition) {
// return savedPosition
// } else {
// return { top: 0 }
// }
// },
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
VueScrollTo.scrollTo(to.hash)
return {}
} else if (savedPosition) {
return savedPosition
} else {
VueScrollTo.scrollTo('#app')
return {}
}
},
})
export default router
<template>
<v-app>
<v-app-bar prominent :elevation="4" color="orange accent-1">
<v-app-bar-nav-icon variant="text" @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<v-toolbar-title>Vuetify</v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<v-navigation-drawer
v-model="drawer"
clipped
theme="dark"
color="deep-orange darken-4"
>
<v-list
>
<v-list-item
exact
v-for="(item, i) in items"
:key=i
:to=item.to
>
<v-list-item-title v-text="item.title"></v-list-item-title>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-main>
<div class="pa-3">
<router-view></router-view>
</div>
</v-main>
</v-app>
</template>
<script setup>
import { ref } from 'vue'
const drawer = ref(null)
const items = [
{
title: 'Home',
to: '/',
},
{
title: 'About',
to: '/about',
},
]
</script>
<style scoped>
html {
overflow: auto !important;
}
</style>
<v-main>の中に<router-view>を配置しないといけないのね。
<template>
<div>
<h2>Button</h2>
<v-btn color="primary" class="mr-2 mb-2">
Normal
</v-btn>
<v-btn color="secondary" flat class="mr-2 mb-2">
Secondary
</v-btn>
<v-btn variant="outlined" color="error" class="mr-2 mb-2">
Error
</v-btn>
<v-btn disabled class="mr-2 mb-2">
Disabled
</v-btn>
<v-btn color="success" class="mr-2 mb-2">
Success
<v-icon
dark
right
>
mdi-checkbox-marked-circle
</v-icon>
</v-btn>
</div>
<div class="mt-3">
<h2 class="mt-3">Icon</h2>
<v-icon icon="mdi-home" class="mr-2"></v-icon>
<v-icon icon="mdi-login" color="error" class="mr-2" />
<v-icon icon="mdi-account" color="success" class="mr-2" />
<v-icon icon="mdi-account" class="mr-2" size="x-large" />
<v-btn
icon="mdi-heart"
color="pink"
class="mx-2"
></v-btn>
<v-btn
color="primary"
variant="outlined"
icon="mdi-pencil"
size="x-large"
></v-btn>
</div>
<div class="mt-3">
<h2 class="mt-3">Alert</h2>
<v-alert type="success" class="mb-1">I'm a success alert.</v-alert>
<v-alert type="info" class="mb-1">I'm an info alert.</v-alert>
<v-alert type="warning" class="mb-1">I'm a warning alert.</v-alert>
<v-alert type="error" class="mb-1">I'm an error alert.</v-alert>
</div>
<div class="mt-3">
<h2 class="mt-3">Card</h2>
<v-card
max-width="344"
variant="outlined"
>
<v-card-item>
<div>
<div class="text-overline mb-1">
OVERLINE
</div>
<div class="text-h6 mb-1">
Headline
</div>
<div class="text-caption">Greyhound divisely hello coldly fonwderfully</div>
</div>
</v-card-item>
<v-card-actions>
<v-btn variant="outlined">
Button
</v-btn>
</v-card-actions>
</v-card>
</div>
</template>
<template>
<h1>About</h1>
<v-card
max-width="300"
variant="outlined"
class="mb-2"
>
<v-list density="compact">
<v-list-item><router-link to="#sample">Sample</router-link></v-list-item>
<v-list-item><router-link to="#example">Example</router-link></v-list-item>
</v-list>
</v-card>
<p>about</p>
<p>about</p>
<p>about</p>
<p>about</p>
<p>about</p>
<p>about</p>
<h2 id="sample">Sample</h2>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<p>sample</p>
<h2 id="example">Example</h2>
<p>example</p>
<p>example</p>
<p>example</p>
<p>example</p>
<p>example</p>
<p>example</p>
<p>example</p>
<p>example</p>
<p>example</p>
…
</template>
http://localhost/app/