Теперь, когда мы закончили с бэкендом, пришло время создать фронтенд нашего приложения. В этом уроке мы будем использовать Vue.js для создания фронтенд-приложения. Опять же, давайте начнем с краткого обзора. Если вы никогда раньше не использовали этот фреймворк, пожалуйста, сначала изучите учебник Vue.js For Beginners.
Вы можете скачать исходный код этого учебника здесь:
Скачать исходный код
Краткий обзор Vue.js
Vue.js — это front-end JavaScript фреймворк, который предоставляет нам простую систему, основанную на компонентах, что позволяет нам создавать сложные пользовательские интерфейсы. Компонентная система означает, что корневой компонент (App.vue
) может импортировать другие компоненты (файлы с расширением .vue
), а эти другие компоненты могут импортировать больше компонентов, что позволяет нам создавать очень сложные системы.
Типичный файл Vue содержит три секции, секция <template>
включает HTML-коды, секция <script>
включает JavaScript-коды, а секция <style>
включает CSS-коды.
В разделе <script>
мы можем объявить новые привязки внутри функции data()
. Затем эти привязки могут быть отображены в разделе <template>
с помощью синтаксиса двойных фигурных скобок ({{ binding }}
). Привязки, объявленные внутри метода data()
, будут автоматически обернуты в систему реактивности Vue. Это означает, что при изменении значения привязки соответствующий компонент будет автоматически отображаться, без необходимости обновлять страницу.
Раздел <script>
также может содержать методы, отличные от data()
, такие как computed
, props
, methods
и так далее. А <template>
также позволяет нам связывать данные с помощью таких директив, как v-bind
, v-on
и v-model
. Если вы не знаете, что это такое, пожалуйста, сначала изучите этот учебник: Vue.js для начинающих.
Создайте новый проект Vue.js
В учебнике «Vue.js для начинающих» мы установили и создали приложение Vue с помощью инструмента командной строки Vue. В этот раз мы поступим по-другому. Мы будем использовать инструмент для сборки фронтенда под названием Vite (произносится как «veet», французское слово, означающее «быстрый»), который был создан тем же человеком, который создал Vue.js.
Зайдите в папку frontend
и выполните следующую команду:
npm init vue@latest
Вам будет предложено несколько вариантов, для нашего проекта нам нужно добавить только Vue Router:
✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formating? … No / Yes
Scaffolding project in ./<your-project-name>...
Done.
Если вам удобнее работать с языком сильных типов, вы можете установить TypeScript. Если вам нужна автокоррекция кода, вы можете установить ESlint и Prettier.
В результате этого процесса в вашем проекте будет создан файл package.json
, в котором хранятся необходимые пакеты и их версии. Вам необходимо установить эти пакеты в свой проект.
cd <your-project-name>
npm install
npm run dev
Еще один момент, прежде чем мы начнем создавать фронтенд-приложение. В нашем проекте мы используем CSS-фреймворк под названием TailwindCSS. Чтобы установить его, выполните следующую команду:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
В результате будут созданы два файла, tailwind.config.js
и postcss.config.js
. Это не учебник по CSS или Tailwind, я предполагаю, что вы уже знаете, как их использовать, и что такое PostCSS. Если нет, пожалуйста, прочитайте их документацию. Tailwind: (https://tailwindcss.com/docs/editor-setup). PostCSS: (https://github.com/postcss/postcss/tree/main/docs).
Перейдите в tailwind.config.js
, и добавьте путь ко всем файлам вашего шаблона:
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Создайте файл ./src/index.css
и добавьте директивы @tailwind
для каждого из слоев Tailwind.
@tailwind base;
@tailwind components;
@tailwind utilities;
Импортируйте вновь созданный файл ./src/index.css
в файл ./src/main.js
.
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './main.css'
const app = createApp(App)
app.use(router)
app.mount('#app')
Теперь вы должны иметь возможность использовать Tailwind внутри файлов .vue
. Давайте проверим это.
<template>
<header>
...
<div class="wrapper">
<HelloWorld msg="You did it!" />
<h1 class="text-3xl font-bold underline">Hello world!</h1>
...
</div>
</header>
...
</template>
Мы добавили заголовок <h1>
после <HelloWorld>
, и этот заголовок использует классы Tailwind.
Маршрутизатор Vue
Также обратите внимание, что на этот раз каталог нашего проекта немного отличается.
Внутри каталога src
находятся папки router
и views
. Каталог router
содержит файл index.js
. Здесь мы можем определить наши маршрутизаторы. Каждый маршрутизатор будет указывать на компонент представления, который находится в каталоге views
, а представление может расширяться на другие компоненты в каталоге components
.
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
}
]
})
export default router
Чтобы вызвать определенный маршрутизатор, загляните в файл App.vue
. Вместо тега <a>
мы используем тег <RouterLink>
, который импортируется из пакета vue-router
.
<script setup>
import { RouterLink, RouterView } from "vue-router";
...
</script>
<template>
<header>
...
<div class="wrapper">
...
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header>
<RouterView />
</template>
Во время рендеринга страницы тег <RouterView />
будет заменен соответствующим представлением.
Если вы не хотите импортировать эти компоненты, просто используйте вместо них <router-link to="">
и <router-view>
. Лично я предпочитаю этот способ, потому что всегда забываю их импортировать.
Создание маршрутов
Для нашего блог-приложения нам нужно создать как минимум 6 страниц. Нам нужна главная страница, которая отображает список последних страниц, страница категорий/тегов, которая отображает все категории/теги, страница категорий/тегов, которая отображает список постов, принадлежащих данной категории/тегу, и, наконец, страница поста, которая отображает содержание поста, а также комментарии.
Итак, вот какие маршрутизаторы я создал.
import { createRouter, createWebHistory } from "vue-router";
import HomeView from "@/views/main/Home.vue";
import PostView from "@/views/main/Post.vue";
import CategoryView from "@/views/main/Category.vue";
import TagView from "@/views/main/Tag.vue";
import AllCategoriesView from "@/views/main/AllCategories.vue";
import AllTagsView from "@/views/main/AllTags.vue";
const routes = [
{
path: "/",
name: "Home",
component: HomeView,
},
{
path: "/category",
name: "Category",
component: CategoryView,
},
{
path: "/tag",
name: "Tag",
component: TagView,
},
{
path: "/post",
name: "Post",
component: PostView,
},
{
path: "/categories",
name: "Categories",
component: AllCategoriesView,
},
{
path: "/tags",
name: "Tags",
component: AllTagsView,
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
Обратите внимание, что в этой статье мы создаем только интерфейс фронтенда, мы пока не занимаемся передачей данных, поэтому не беспокойтесь о том, как найти нужный пост/категорию/тег прямо сейчас.
Представления и компоненты
Это внешний пользовательский интерфейс, который я создал для этого проекта, вы можете использовать мой код напрямую или, если он вам не нравится, вы можете следовать этому руководству по Vue.js (Vue.js для начинающих) и создать свой собственный.
- Изображения
<template>
<div class="container mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0">
<div class="flex flex-col justify-between h-screen">
<header class="flex flex-row items-center justify-between py-10">
<div class="nav-logo text-2xl font-bold">
<router-link to="/">Django Vue Starter Blog</router-link>
</div>
<div class="nav-links hidden sm:block">
<router-link
to="/"
class="mx-2 font-sans font-medium hover:underline hover:text-teal-700"
>Home</router-link
>
<router-link
to="/categories"
class="mx-2 font-sans font-medium hover:underline hover:text-teal-700"
>Category</router-link
>
<router-link
to="/tags"
class="mx-2 font-sans font-medium hover:underline hover:text-teal-700"
>Tag</router-link
>
</div>
</header>
<router-view />
<footer class="flex flex-col place-items-center mt-5 py-5 border-t-2">
<div class="mb-3 flex space-x-4">
<i
class="fa-brands fa-linkedin text-3xl text-gray-700 hover:text-teal-700"
></i>
...
</div>
<div class="mb-3 flex space-x-1 text-sm text-gray-700">
<div>
<a
href="<https://www.ericsdevblog.com>"
class="hover:underline hover:text-teal-700"
>Eric Hu</a
>
</div>
<div>•</div>
<div>© 2022</div>
<div>•</div>
<a href="/" class="hover:underline hover:text-teal-700"
>Vue.js Starter Blog</a
>
</div>
</footer>
</div>
</div>
</template>
<script>
export default {
...
};
</script>
<template>
<div class="home">
<h1 class="text-5xl font-extrabold mb-2">Recent Posts</h1>
<p class="text-gray-500 text-lg mb-5">
A blog created with Django, Vue.js and TailwindCSS
</p>
<post-list></post-list>
</div>
</template>
<script>
// @ is an alias to /src
import PostList from "@/components/PostList.vue";
export default {
components: { PostList },
name: "HomeView",
};
</script>
<template>
<div class="flex flex-col place-content-center place-items-center">
<div class="py-8 border-b-2">
<h1 class="text-5xl font-extrabold">All Categories</h1>
</div>
<div class="flex flex-wrap py-8">
<router-link
class="my-2 mr-5 text-sm font-medium uppercase text-teal-500 hover:underline hover:text-teal-700"
to="/category"
>Category Name</router-link
>
<router-link
class="my-2 mr-5 text-sm font-medium uppercase text-teal-500 hover:underline hover:text-teal-700"
to="/category"
>Category Name</router-link
>
...
</div>
</div>
</template>
<script>
export default {
name: "CategoriesView",
};
</script>
<template>
<div>
<h1 class="text-5xl font-extrabold mb-2">Category Name</h1>
<p class="text-gray-500 text-lg mb-5">
A blog created with Django, Vue.js and TailwindCSS
</p>
<post-list></post-list>
</div>
</template>
<script>
// @ is an alias to /src
import PostList from "@/components/PostList.vue";
export default {
components: { PostList },
name: "CategoryView",
};
</script>
<template>
<div class="home">
<div class="flex flex-col place-items-center border-b-2">
<!-- Featured Image and title -->
<img src="..." class="w-full my-5" />
<h1 class="text-center text-5xl font-extrabold mb-5">
Post Title
</h1>
<p class="text-gray-500 text-lg mb-2">
March 3, 2022 - By Eric Hu
</p>
</div>
<!-- Tags -->
<div class="flex flex-wrap my-4">
<div class="mr-5 text-sm font-medium">Tags:</div>
<router-link
class="mr-5 text-sm font-medium uppercase text-teal-500 hover:underline hover:text-teal-700"
to="/tag"
>Tag Name</router-link
>
...
</div>
<!-- Main content -->
<div class="py-5 font-serif space-y-4">
Lorem Lipsum ...
</div>
<!-- Like, Comment and Share -->
...
<!-- Comment Section -->
...
</div>
</template>
<script></script>
<template>
<div class="post-list">
<ul v-if="publishedPosts" class="divide-y divide-gray-200">
<li class="py-12">
<article>
<div
class="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0"
>
<dl>
<dt class="sr-only">Published on</dt>
<dd
class="text-base font-medium leading-6 text-gray-500 dark:text-gray-400"
>
<time>March 3, 2022</time>
</dd>
</dl>
<div class="space-y-5 xl:col-span-3">
<div class="space-y-6">
<div>
<h2 class="text-2xl font-bold leading-8 tracking-tight">
<router-link
class="text-gray-900"
to="/post"
>This is a post title</router-link
>
</h2>
<router-link
class="text-sm font-medium uppercase text-teal-500 hover:underline hover:text-teal-700"
to="/category"
>Category</router-link
>
</div>
<div class="prose max-w-none text-gray-500">
Lorem Lipsum ...
</div>
</div>
<div class="text-base font-medium leading-6">
<router-link
class="text-teal-500 hover:underline hover:text-teal-700"
to="/post"
>Read more →</router-link
>
</div>
</div>
</div>
</article>
</li>
...
</ul>
</div>
</template>
<script>
export default {
name: "PostListComponent",
};
</script>