译: 简洁干净的 Vue 应用布局结构
文章翻译至: Clean Layout Architecture for Vue Applications
目录
合理的设计布局是减少重复代码和创建可维护的应用程序的基本做饭。如果您使用 Nuxt,则会提供一个开箱即用的优雅解决方案。但不幸的是,在 Vue 中,官方文档没有解决这些问题。这些问题在多个应用程序中非常常见。
经过多次尝试,我得出了一个运行良好且可轻松扩展的架构。让我用一个小的概念证明来演示它。
要求
首先,让我们建立这个布局需要满足的一些规则:
需要声明页面每个部分的布局和组件
对页面的更改不应影响其他页面
如果布局的某些部分能跨页面通用,则它们应该只声明一次
安装 Vue Router
npm i vue-router@4
声明路由:
const routes = [
{ path: "/", component: () => import("./pages/HomePage.vue") },
{ path: "/explore", component: () => import("./pages/ExplorePage.vue") },
];
安装插件:
import { createApp } from "vue";
import { createRouter, createWebHashHistory } from "vue-router";
import App from "./App.vue";
import routes from "./routes.ts"
const router = createRouter({
history: createWebHashHistory(),
routes,
});
const app = createApp(App);
app.use(router);
app.mount("#app");
最后更新App.vue
<template>
<router-view />
</template>
我们现在可以在两个页面之间导航:
The Pages
我们将创建以下page:Home、Explore、Article和 404。以及三种布局:三栏、两栏和空白。
Home 是每个流行的社交网络应用都使用的典型三栏布局。第一列包含应用程序的logo和导航,在使用此布局的每个页面上都保持不变。这也适用于右下角的Footer。每个页面的main content和侧边栏小部件都会发生变化。
首先实现 HomePage.vue:
<template>
<ThreeColumnLayout>
<h2 class="text-3xl font-bold mb-4">Homepage content</h2>
<ArticleCard v-for="article in articles" :article="article" />
<template #aside>
<WidgetFriendSuggestions />
</template>
</ThreeColumnLayout>
</template>
<script setup lang="ts">
import ThreeColumnLayout from "../layouts/ThreeColumnLayout.vue";
import ArticleCard from "../components/ArticleCard.vue";
import WidgetFriendSuggestions from "../components/WidgetFriendSuggestions.vue";
import useArticles from "../composables/useArticles";
const articles = useArticles().getArticles();
</script>
<style scoped></style>
接着实现 <ThreeColumnLayout>
组件。其默认插槽包括标题和文章列表,它们是页面contenet部分。此外,我们还有一个名为 aside
的具名插槽,用于声明 widget。
三栏布局
一般的,<ThreeColumnLayout>
组件放置在文件夹 /layouts 内。它将使用网格容器并利用 grid-template-areas
创建三列布局。
布局的实现细节将是该组件而不是页面需要关注的。使用 Flexbox、Grid 或任何其他技术实现都可以。
此布局有 3 列
第一列将包含硬编码的logo和导航组件
第二列将仅创建默认插槽,插槽内容将由页面来决定。
第三列将包含aside插槽和每个页面通用的Footer组件
<template>
<div class="three-column-layout">
<header>
<AppLogo />
<AppNavigation />
</header>
<main>
<slot />
</main>
<aside>
<slot name="aside" />
<AppFooter />
</aside>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
.three-column-layout {
display: grid;
grid-template-areas:
"header"
"main"
"aside";
header {
grid-area: header;
margin-top: 30px;
}
main {
grid-area: main;
margin-top: 10px;
padding: 20px;
}
aside {
grid-area: aside;
margin-top: 10px;
padding: 20px;
}
@media (min-width: 768px) {
grid-template-columns: 1fr 3fr 1fr;
grid-template-areas: "header main aside";
}
}
</style>
创建具有相同布局的新页面时将证明这种方法是多么简洁。
默认插槽为main content,并在aside上添加一个附加小部件:
两栏布局
对于 ExplorePage.vue ,我们将创建一个两列布局。第一列与三列布局相同,但content将占据屏幕的其余部分,并且Footer位于底部。
该实现看起来与之前的没有太大不同。但这次我们使用 flex
和 flex-basis
只是为了演示创建 CSS 布局的不同方法。在现实场景中,所有实现都应该尽可能使用相同的技术。
<script setup>
import AppNavigation from "../components/AppNavigation.vue";
import AppLogo from "../components/AppLogo.vue";
import AppFooter from "../components/AppFooter.vue";
</script>
<template>
<div class="two-column-layout">
<header>
<AppLogo />
<AppNavigation />
</header>
<main>
<slot />
<AppFooter />
</main>
</div>
</template>
<style scoped>
.two-column-layout {
display: flex;
@media (max-width: 768px) {
flex-direction: column;
}
}
header {
flex-basis: 20%;
margin-top: 30px;
}
main {
flex-basis: 80%;
margin-top: 10px;
padding: 20px;
}
</style>
使用两栏布局:
<script setup>
import TwoColumnLayout from "../layouts/TwoColumnLayout.vue";
import ExploreItem from "../components/ExploreItem.vue";
import useArticles from "../composables/useArticles";
const articles = useArticles().getExploreArticles();
</script>
<template>
<TwoColumnLayout>
<h2 class="text-3xl font-bold mb-12">
Latest content on <a target="_blank" href="https://dev.to/">Dev.to</a>
</h2>
<div class="grid lg:grid-cols-3 gap-6">
<ExploreItem v-for="article in articles" :article="article" />
</div>
</TwoColumnLayout>
</template>
使用这种布局的ExplorePage页面效果:
最后,让我们创建一个可以在 404 页面上使用的空白布局。
<template>
<main class="container my-24 px-6 mx-auto">
<slot />
</main>
</template>
实现很简单,只有一个居中的容器。
通过这种方式,我们可以保持页面组件的精简,并确保使用此布局的多个页面的外观和行为相同。
<script setup>
import BlankLayout from "../layouts/BlankLayout.vue";
import PageNotFound from "../components/PageNotFound.vue";
</script>
<template>
<BlankLayout>
<PageNotFound />
</BlankLayout>
</template>
End.