使用 Django、Vue 和 GraphQL 构建博客(3)
第 7 步:创建 Vue 组件
现在您已经启动并运行了 Vue 并运行了将到达您的组件的路由,您可以开始创建最终显示来自 GraphQL 端点的数据的组件。目前,您只需让它们显示一些静态内容。下表描述了您将创建的组件:
成分 | 显示器 |
---|---|
AuthorLink |
指向给定作者页面的链接(在Post 和 中使用PostList ) |
PostList |
博客文章的一个给定的列表(在使用AllPosts ,Author 和PostsByTag ) |
AllPosts |
所有帖子的列表,最新的在前 |
PostsByTag |
与给定标签关联的帖子列表,最新的在前 |
Post |
给定帖子的元数据和内容 |
Author |
关于作者的信息和他们所写的帖子列表 |
您将在下一步中使用动态数据更新这些组件。
该AuthorLink
组件
您将创建的第一个组件显示指向作者的链接。
AuthorLink.vue
在src/components/
目录中创建一个文件。此文件是 Vue 单文件组件 (SFC)。SFC 包含正确呈现组件所需的 HTML、JavaScript 和 CSS。
在AuthorLink
接受一个author
支柱,其结构相当于约你GraphQL API在作者的数据。该组件应显示用户的名字和姓氏(如果提供),否则显示用户的用户名。
您的AuthorLink.vue
文件应如下所示:
<template>
<router-link
:to="`/author/${author.user.username}`"
>{{ displayName }}</router-link>
</template>
<script>
export default {
name: 'AuthorLink',
props: {
author: {
type: Object,
required: true,
},
},
computed: {
displayName () {
return (
this.author.user.firstName &&
this.author.user.lastName &&
`${this.author.user.firstName} ${this.author.user.lastName}`
) || `${this.author.user.username}`
},
},
}
</script>
该组件不会直接使用 GraphQL。相反,其他组件将使用author
prop传入作者信息。
该PostList
组件
该PostList
组件接受一个posts
prop,其结构对应于你的 GraphQL API 中关于帖子的数据。该组件还接受一个布尔 showAuthor
属性,您将false
在作者的页面上设置它,因为它是冗余信息。该组件应显示以下功能:
- 帖子的标题和副标题,将它们链接到帖子页面
- 指向帖子作者的链接
AuthorLink
(如果showAuthor
是true
) - 发布日期
- 帖子的元描述
- 与帖子关联的标签列表
PostList.vue
在src/components/
目录中创建SFC 。组件模板应如下所示:
<template>
<div>
<ol class="post-list">
<li class="post" v-for="post in publishedPosts" :key="post.title">
<span class="post__title">
<router-link
:to="`/post/${post.slug}`"
>{{ post.title }}: {{ post.subtitle }}</router-link>
</span>
<span v-if="showAuthor">
by <AuthorLink :author="post.author" />
</span>
<div class="post__date">{{ displayableDate(post.publishDate) }}</div>
<p class="post__description">{{ post.metaDescription }}</p>
<ul>
<li class="post__tags" v-for="tag in post.tags" :key="tag.name">
<router-link :to="`/tag/${tag.name}`">#{{ tag.name }}</router-link>
</li>
</ul>
</li>
</ol>
</div>
</template>
该PostList
组件的JavaScript看起来应该像下面这样:
<script>
import AuthorLink from '@/components/AuthorLink'
export default {
name: 'PostList',
components: {
AuthorLink,
},
props: {
posts: {
type: Array,
required: true,
},
showAuthor: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
publishedPosts () {
return this.posts.filter(post => post.published)
}
},
methods: {
displayableDate (date) {
return new Intl.DateTimeFormat(
'en-US',
{ dateStyle: 'full' },
).format(new Date(date))
}
},
}
</script>
该PostList
组件接收其数据,prop
而不是直接使用 GraphQL。
您可以添加一些可选的 CSS 样式,以使帖子列表在呈现后更具可读性:
<style>
.post-list {
list-style: none;
}
.post {
border-bottom: 1px solid #ccc;
padding-bottom: 1rem;
}
.post__title {
font-size: 1.25rem;
}
.post__description {
color: #777;
font-style: italic;
}
.post__tags {
list-style: none;
font-weight: bold;
font-size: 0.8125rem;
}
</style>
这些样式增加了一些间距,消除了一些混乱,并区分了不同的信息片段以帮助提高可扫描性。
该AllPosts
组件
您将创建的下一个组件是博客上所有帖子的列表。它需要显示两条信息:
- 最近的帖子标题
- 帖子列表,使用
PostList
AllPosts.vue
在src/components/
目录中创建SFC 。它应该如下所示:
<template>
<div>
<h2>Recent posts</h2>
<PostList v-if="allPosts" :posts="allPosts" />
</div>
</template>
<script>
import PostList from '@/components/PostList'
export default {
name: 'AllPosts',
components: {
PostList,
},
data () {
return {
allPosts: null,
}
},
}
</script>
allPosts
在本教程后面,您将使用 GraphQL 查询动态填充变量。
该PostsByTag
组件
该PostsByTag
组件与组件非常相似AllPosts
。标题文本不同,您将在下一步中查询不同的帖子集。
PostsByTag.vue
在src/components/
目录中创建SFC 。它应该如下所示:
<template>
<div>
<h2>Posts in #{{ $route.params.tag }}</h2>
<PostList :posts="posts" v-if="posts" />
</div>
</template>
<script>
import PostList from '@/components/PostList'
export default {
name: 'PostsByTag',
components: {
PostList,
},
data () {
return {
posts: null,
}
},
}
</script>
posts
在本教程后面,您将使用 GraphQL 查询填充变量。
该Author
组件
该Author
组件充当作者的个人资料页面。它应该显示以下信息:
- 带有作者姓名的标题
- 指向作者网站的链接(如果提供)
- 作者的传记,如果提供
- 作者的帖子列表,
showAuthor
设置为false
现在Author.vue
在src/components/
目录中创建SFC 。它应该如下所示:
<template>
<div v-if="author">
<h2>{{ displayName }}</h2>
<a
:href="author.website"
target="_blank"
rel="noopener noreferrer"
>Website</a>
<p>{{ author.bio }}</p>
<h3>Posts by {{ displayName }}</h3>
<PostList :posts="author.postSet" :showAuthor="false" />
</div>
</template>
<script>
import PostList from '@/components/PostList'
export default {
name: 'Author',
components: {
PostList,
},
data () {
return {
author: null,
}
},
computed: {
displayName () {
return (
this.author.user.firstName &&
this.author.user.lastName &&
`${this.author.user.firstName} ${this.author.user.lastName}`
) || `${this.author.user.username}`
},
},
}
</script>
author
在本教程后面,您将使用 GraphQL 查询动态填充变量。
该Post
组件
就像数据模型一样,Post
组件是最有趣的,因为它负责显示所有帖子的信息。该组件应显示有关帖子的以下信息:
- 标题和副标题,作为标题
- 作者,作为链接使用
AuthorLink
- 发布日期
- 元描述
- 内容主体
- 关联标签列表,作为链接
由于您的数据建模和组件架构,您可能会惊讶于这需要的代码如此之少。Post.vue
在src/components/
目录中创建SFC 。它应该如下所示:
<template>
<div class="post" v-if="post">
<h2>{{ post.title }}: {{ post.subtitle }}</h2>
By <AuthorLink :author="post.author" />
<div>{{ displayableDate(post.publishDate) }}</div>
<p class="post__description">{{ post.metaDescription }}</p>
<article>
{{ post.body }}
</article>
<ul>
<li class="post__tags" v-for="tag in post.tags" :key="tag.name">
<router-link :to="`/tag/${tag.name}`">#{{ tag.name }}</router-link>
</li>
</ul>
</div>
</template>
<script>
import AuthorLink from '@/components/AuthorLink'
export default {
name: 'Post',
components: {
AuthorLink,
},
data () {
return {
post: null,
}
},
methods: {
displayableDate (date) {
return new Intl.DateTimeFormat(
'en-US',
{ dateStyle: 'full' },
).format(new Date(date))
}
},
}
</script>
post
在本教程后面,您将使用 GraphQL 查询动态填充变量。
该App
组件
在您看到您的劳动成果之前,您需要更新App
您的 Vue setup 命令创建的组件。它应该显示AllPosts
组件,而不是显示 Vue 启动页面。
App.vue
在src/
目录中打开SFC 。您可以删除其中的所有内容,因为您需要将其替换为显示以下功能的代码:
- 带有链接到主页的博客标题的标题
<router-view>
, Vue Router 组件,为当前路由渲染正确的组件
您的App
组件应如下所示:
<template>
<div id="app">
<header>
<router-link to="/">
<h1>Awesome Blog</h1>
</router-link>
</header>
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
您还可以添加一些可选的 CSS 样式来稍微修饰一下显示:
<style>
* {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 1.5rem;
}
* + * {
margin-top: 1.5rem;
}
#app {
margin: 0;
padding: 0;
}
</style>
这些样式为页面上的大多数元素提供了一些喘息的空间,并删除了大多数浏览器默认添加的整个页面周围的空间。
步骤 7 总结
如果您之前没有经常使用 Vue,这一步可能需要消化很多。不过,您已经达到了一个重要的里程碑。您有一个可运行的 Vue 应用程序,其中包含准备好显示数据的路由和视图。
您可以通过启动 Vue 开发服务器并访问http://localhost:8080
. 您应该会看到博客的标题和最近的帖子标题。如果您这样做了,那么您已准备好进行最后一步,在此步骤中您将使用 Apollo 查询您的 GraphQL API 以将前端和后端结合在一起。
第 8 步:获取数据
现在您已经准备好在数据可用时显示数据,是时候从您的 GraphQL API 中获取该数据了。
Apollo 使查询 GraphQL API 更加方便。您之前安装的Vue Apollo插件将 Apollo 集成到 Vue 中,这使得从 Vue 项目中查询 GraphQL 变得更加方便。
配置 Vue Apollo
Vue Apollo 大多是开箱即用的配置,但您需要告诉它要查询的正确端点。您可能还想关闭它默认尝试使用的 WebSocket 连接,因为这会在浏览器的网络和控制台选项卡中产生干扰。编辑模块中的apolloProvider
定义src/main.js
以指定httpEndpoint
和wsEndpoint
属性:
new Vue({
...
apolloProvider: createProvider({
httpEndpoint: 'http://localhost:8000/graphql',
wsEndpoint: null,
}),
...
})
现在您已准备好开始添加查询以填充您的页面。您将通过向created()
多个 SFC添加一个函数来实现此目的。created()
是一个特殊的Vue 生命周期钩子,当组件即将在页面上呈现时执行。您可以使用此挂钩来查询要呈现的数据,以便在您的组件呈现时可以使用这些数据。您将为以下组件创建查询:
Post
Author
PostsByTag
AllPosts
您可以从创建Post
查询开始。
该Post
查询
对单个帖子的查询接受slug
所需帖子的 。它应该返回所有必要的信息以显示帖子信息和内容。
您将使用$apollo.query
helper 和gql
helper 在Post
组件的created()
函数中构建查询,最终使用响应来设置组件的post
以便可以呈现它。created()
应该如下所示:
<script>
import gql from 'graphql-tag'
...
export default {
...
async created () {
const post = await this.$apollo.query({
query: gql`query ($slug: String!) {
postBySlug(slug: $slug) {
title
subtitle
publishDate
metaDescription
slug
body
author {
user {
username
firstName
lastName
}
}
tags {
name
}
}
}`,
variables: {
slug: this.$route.params.slug,
},
})
this.post = post.data.postBySlug
},
...
}
</script>
此查询获取有关帖子及其相关作者和标签的大部分数据。请注意,$slug
占位符在查询中使用,variables
传递给的属性$apollo.query
用于填充占位符。该slug
属性$slug
按名称与占位符匹配。您将在其他一些查询中再次看到此模式。
该Author
查询
而在查询中,Post
您获取了单个帖子的数据和有关作者的一些嵌套数据,而在Author
查询中,您需要获取作者数据和作者的所有帖子的列表。
作者查询接受username
所需作者的 ,并应返回所有必要的信息以显示作者及其帖子列表。它应该如下所示:
<script>
import gql from 'graphql-tag'
...
export default {
...
async created () {
const user = await this.$apollo.query({
query: gql`query ($username: String!) {
authorByUsername(username: $username) {
website
bio
user {
firstName
lastName
username
}
postSet {
title
subtitle
publishDate
published
metaDescription
slug
tags {
name
}
}
}
}`,
variables: {
username: this.$route.params.username,
},
})
this.author = user.data.authorByUsername
},
...
}
</script>
此查询使用postSet
,如果您过去做过一些 Django 数据建模,它可能看起来很熟悉。“post set”这个名字来自 Django 为ForeignKey
字段创建的反向关系。在这种情况下,该帖子与其作者具有外键关系,该关系与名为 的帖子具有反向关系post_set
。Graphene-Django 已经像postSet
在 GraphQL API 中一样自动公开了这一点。
该PostsByTag
查询
查询PostsByTag
应该与您创建的第一个查询非常相似。此查询接受所需tag
并返回匹配帖子的列表。created()
应该如下所示:
<script>
import gql from 'graphql-tag'
...
export default {
...
async created () {
const posts = await this.$apollo.query({
query: gql`query ($tag: String!) {
postsByTag(tag: $tag) {
title
subtitle
publishDate
published
metaDescription
slug
author {
user {
username
firstName
lastName
}
}
tags {
name
}
}
}`,
variables: {
tag: this.$route.params.tag,
},
})
this.posts = posts.data.postsByTag
},
...
}
</script>
您可能会注意到,每个查询的某些部分看起来非常相似。尽管本教程不会介绍它,但您可以使用GraphQL 片段来减少查询代码中的重复。
该AllPosts
查询
查询AllPosts
不需要任何输入信息并返回与PostsByTag
查询相同的信息集。它应该如下所示:
<script>
import gql from 'graphql-tag'
export default {
...
async created () {
const posts = await this.$apollo.query({
query: gql`query {
allPosts {
title
subtitle
publishDate
published
metaDescription
slug
author {
user {
username
firstName
lastName
}
}
tags {
name
}
}
}`,
})
this.allPosts = posts.data.allPosts
},
...
}
</script>
这是目前的最后一个查询,但您应该重新访问最后几个步骤以让它们下沉。如果您想在未来添加具有新数据视图的新页面,则需要创建一个路由、一个组件, 和查询。
步骤 8 总结
既然每个组件都在获取它需要显示的数据,那么您就完成了一个正常运行的博客。运行 Django 开发服务器和 Vue 开发服务器。访问http://localhost:8080
并浏览您的博客。如果您可以在浏览器中看到作者、帖子、标签和帖子内容,那么您就是金子!
下一步
您首先创建了一个 Django 博客后端来管理、保存和提供博客数据。然后您创建了一个 Vue 前端来使用和显示该数据。您使用 Graphene 和 Apollo 使两者与 GraphQL 通信。
您可能已经想知道下一步可以做什么。要进一步验证您的博客是否按预期运行,您可以尝试以下操作:
- 添加更多用户和帖子以查看它们按作者拆分。
- 取消发布一些帖子以确认它们不会出现在博客上。
如果您对自己的工作充满信心和冒险精神,您还可以进一步使用您的这个系统:
- 扩展数据模型以在 Django 博客中创建新行为。
- 创建新查询以提供有关您博客数据的有趣视图。
- 探索 GraphQL 突变以在读取数据的同时写入数据。
- 将 CSS 添加到您的单文件组件中,使博客更加引人注目。
您放在一起的数据建模和组件体系结构非常具有可扩展性,因此您可以随心所欲地使用它!
如果您想让 Django 应用程序为黄金时段做好准备,请阅读将Django + Python3 + PostgreSQL 部署到 AWS Elastic Beanstalk或在 Fedora 上开发和部署 Django。您也可以使用 Amazon Web Services 或Netlify 之类的工具来部署您的 Vue 项目。
结论
您已经了解了如何使用 GraphQL 构建类型化的、灵活的数据视图。您可以在已构建或计划构建的现有 Django 应用程序上使用这些相同的技术。与其他 API 一样,您也可以在大多数客户端框架中使用您的 API。
在本教程中,您学习了如何:
- 构建 Django 博客数据模型和管理界面
- 使用 Graphene-Django将数据模型包装在GraphQL API 中
- 为每个数据视图创建并路由到单独的Vue 组件
- 使用 Apollo 动态查询 GraphQL API以填充您的 Vue 组件
您涵盖了很多基础知识,因此请尝试确定一些新方法,以便在不同的上下文中使用这些概念来巩固您的学习。快乐编码,快乐写博客!
- 点赞
- 收藏
- 关注作者
评论(0)