Laravel 11 і Vue.js: маршрутизація у Vue.js
Laravel 11 і Vue.js — це чудове поєднання для написання чистого та ефективного коду.
У цьому підручнику ми разом побачимо, як налаштувати та керувати маршрутизацією за допомогою Vue.js у Laravel 11, Vue Routes.
Приблизний час читання: 5 хвилин
Приклад, описаний у цій статті, доступний за адресою Github.
Щоб прочитати та повністю зрозуміти цю статтю, рекомендуємо прочитати статтю Laravel 11 і Vue.js: встановлення, налаштування та приклади, а реалізацію коду можна знайти за адресою Репозиторій GitHUB.
Для реалізації routes
in vue
, спочатку нам потрібно налаштувати vue-router
.
Встановлення та ввімкнення vue-router
Спочатку нам потрібно встановити пакет vue-router
, і для цього ми використовуємоnpm install
:
npm install vue-router@latest
Потім ми переходимо до імпорту vue-router
безпосередньо в app.js
:
ресурси/js/app.js:
import './bootstrap';
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import PostsIndex from './components/Posts/Index.vue'
createApp({})
.component('PostsIndex', PostsIndex)
.mount('#app')
Тепер нам потрібно створити список routes
, ініціалізуйте router
і ввімкніть його в межах createApp()
.
Крім того, нам не потрібно визначати компоненти в createApp()
, оскільки вони вже визначені в routes
.
ресурси/js/app.js:
import './bootstrap';
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import PostsIndex from './components/Posts/Index.vue'
const routes = [
{ path: '/', component: PostsIndex },
]
const router = createRouter({
history: createWebHistory(),
routes
})
createApp({})
.use(router)
.mount('#app')
Компонент Vue для другої сторінки
Тепер давайте створимо компонент Vue для сторінки створення публікації та додайте фіктивний текст.
ресурси/js/components/Posts/Create.vue:
<template>
To-do.
</template>
Далі ми можемо створити новий шлях і вказати його на цей компонент Vue.
ресурси/js/app.js:
import './bootstrap';
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import PostsIndex from './components/Posts/Index.vue'
import PostsCreate from './components/Posts/Create.vue'
const routes = [
{ path: '/', component: PostsIndex },
{ path: '/posts/create', component: PostsCreate },
]
const router = createRouter({
history: createWebHistory(),
routes
})
createApp({})
.use(router)
.mount('#app')
Зміна макета: від Blade до Vue
Але зараз ми не можемо додавати нові посилання до навігації, вони знаходяться у файлах Laravel Blade від Breeze а не у файлах Vue. Отже, нам потрібно змінити всю структуру на файл .vue
, включаючи основний макет, щоб ми могли використовувати <router-link>
.
По-перше, ми будемо використовувати resources/views/dashboard.blade.php
як основний файл HTML, де ми встановили id="app"
у тезі <body>
, замість попереднього <div id="app">
.
resources/views/dashboard.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>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="font-sans antialiased" id="app">
</body>
</html>
Далі нам потрібно мати a кореневий компонент: основний макет.
ресурси/js/layouts/App.vue:
<template>
<div class="min-h-screen bg-gray-100">
<nav class="bg-white border-b border-gray-100">
<!-- Primary Navigation Menu -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="/uk/">
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" class="block h-9 w-auto fill-current text-gray-800">
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
</svg>
</a>
</div>
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<router-link to="/" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Posts
</router-link>
<router-link to="/posts/create" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Create Post
</router-link>
</div>
</div>
</div>
</div>
</nav>
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Dashboard
</h2>
</div>
</header>
<!-- Page Content -->
<main>
<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">
<router-view></router-view>
</div>
</div>
</div>
</div>
</main>
</div>
</template>
Обидва <router-link>
що <router-view>
вони походять від vue-router, замінюючи типовий HTML <a href
посилання. Коли хтось натискає ці посилання, оновлюється не вся сторінка, а лише її частина router-view
. Це поведінка SPA.
Il <router-view>
це елемент, куди буде завантажено весь основний динамічний вміст.
Тепер нам потрібно завантажити цей макет під час створення програми Vue.
ресурси/js/app.js:
import './bootstrap';
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './layouts/App.vue'
import PostsIndex from './components/Posts/Index.vue'
import PostsCreate from './components/Posts/Create.vue'
// ...
createApp(App)
.use(router)
.mount('#app')
Тепер, переміщаючись по проекту в браузері, ми можемо побачити два посилання в навігації.
А якщо натиснути Create Post
компонент Vue PostsCreate
буде активовано, а URL-адреса на панелі зміниться.
Підкресліть активне посилання
Підкреслення під активним посиланням здійснюється шляхом зазначення класів active-class
в межах <router-link>
.
<router-link to="/" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Posts
</router-link>
Заборонити завантаження безпосередньо в браузері
Тепер нам залишилося вирішити одну проблему.
Якщо ви спробуєте отримати доступ до адреси /posts/create
безпосередньо через URL-адресу, ви отримаєте помилку 404.
Щоб вирішити цю проблему, нам потрібно додати шлях доПрограми Laravel: робити ставки kvalsiasi шлях до файлу dashboard
di visualizzazione.
routes/web.php:
Route::view('/{any?}', 'dashboard')
->where('any', '.*');
Тепер під час навігації щойно визначеними сторінками код буде завантажено в Vue. Наступним кроком буде проведення очищення маршрути налаштування Vue. Ми запровадимо іменування шляхів і перенесемо шляхи в окремий файл.
Давайте дамо назву routes
Давайте додамо назву routes
.
ресурси/js/app.js:
import './bootstrap';
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './layouts/App.vue'
import PostsIndex from './components/Posts/Index.vue'
import PostsCreate from './components/Posts/Create.vue'
const routes = [
{
path: '/',
name: 'posts.index',
component: PostsIndex
},
{
path: '/posts/create',
name: 'posts.create',
component: PostsCreate
},
]
const router = createRouter({
history: createWebHistory(),
routes
})
createApp(App)
.use(router)
.mount('#app')
В основному плані сapp
, замість того, щоб кодувати посилання статично, ми будемо прив’язувати їх до імені, написання <router-link :to="{ name: 'posts.create' }">
ресурси/js/layouts/App.vue:
<template>
// ...
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<router-link :to="{ name: 'posts.index' }" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Posts
</router-link>
<router-link :to="{ name: 'posts.create' }" active-class="border-b-2 border-indigo-400" class="inline-flex items-center px-1 pt-1 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out">
Create Post
</router-link>
</div>
// ...
</template>
Визначення маршрутів за назвою дає нам гнучкість. Якщо ми вирішимо змінити шлях, нам не потрібно буде вручну змінювати кожен файл, де ми використовували цей шлях.
Шляхи в окремому файлі
Тепер зведемо ці маршрути в окремий файл. Створіть новий файл із наведеним нижче кодом.
ресурси/js/routes/index.js:
import { createRouter, createWebHistory } from 'vue-router';
import PostsIndex from '@/components/Posts/Index.vue'
import PostsCreate from '@/components/Posts/Create.vue'
const routes = [
{
path: '/',
name: 'posts.index',
component: PostsIndex
},
{
path: '/posts/create',
name: 'posts.create',
component: PostsCreate
},
]
export default createRouter({
history: createWebHistory(),
routes
})
Щоб імпортувати цей файл routes
в основному app.js
, скористаємосяexport default
.
І тепер ми можемо імпортувати цей файл routes
в межах main app.js
. Файл app.js
стає таким:
ресурси/js/app.js:
import './bootstrap';
import { createApp } from 'vue'
import App from './layouts/App.vue'
import router from './routes/index'
createApp(App)
.use(router)
.mount('#app')
Тепер ви можете відвідати сторінку, і вона має працювати належним чином. Ми нічого не змінили візуально, але ми додали більше порядку в наш код для наступних реалізацій.
Тепер давайте подивимося, як ми можемо створити глобальні змінні. Тепер на кожній сторінці ми маємо заголовок із написом Інформаційна панель. Давайте спробуємо змінити його динамічно.
Є кілька способів зробити це. Ми передамо цей текст як параметр. Всередині файлу App.vue
давайте змінимо слово Dashboard на атрибут variable.
ресурси/js/layouts/App.vue:
<template>
// ...
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ currentPageTitle }}
</h2>
</div>
</header>
// ...
</template>
Ми зробимо цю змінну обчисленою, обробленою властивістю.
ресурси/js/layouts/App.vue:
<template>
// ...
</template>
<script setup>
import { computed } from 'vue';
const currentPageTitle = computed(() => 'New computed dashboard')
</script>
Тепер давайте динамічно встановимо текст для кожної сторінки. Для цього ми додамо нову змінну meta
.
ресурси/js/routes/index.js:
// ...
const routes = [
{
path: '/',
name: 'posts.index',
component: PostsIndex,
meta: { title: 'Posts' }
},
{
path: '/posts/create',
name: 'posts.create',
component: PostsCreate,
meta: { title: 'Add new post' }
},
]
// ...
Щоб отримати доступ до цієї мета-змінної в основному макеті програми, нам потрібно імпортувати шляхи Composable і використовувати їх для отримання мета-файлу з назвою.
ресурси/js/layouts/App.vue:
<template>
// ...
</template>
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router'
const route = useRoute()
const currentPageTitle = computed(() => route.meta.title)
</script>
Переглядаючи сторінки проекту, ви зможете побачити результати внесених змін і почати завантажувати перші публікації.
Приклад, описаний у цій статті, доступний за адресою Github.
Пов'язані читання
Ercole Palmeri