使用 Django、Vue 和 GraphQL 构建博客(3)

Yuchuan 发表于 2021/12/04 08:48:57 2021/12/04
【摘要】 您已经了解了如何使用 GraphQL 构建类型化的、灵活的数据视图。您可以在已构建或计划构建的现有 Django 应用程序上使用这些相同的技术。与其他 API 一样,您也可以在大多数客户端框架中使用您的 API。

第 7 步:创建 Vue 组件

现在您已经启动并运行了 Vue 并运行了将到达您的组件的路由,您可以开始创建最终显示来自 GraphQL 端点的数据的组件。目前,您只需让它们显示一些静态内容。下表描述了您将创建的组件:

成分 显示器
AuthorLink 指向给定作者页面的链接(在Post和 中使用PostList
PostList 博客文章的一个给定的列表(在使用AllPostsAuthorPostsByTag
AllPosts 所有帖子的列表,最新的在前
PostsByTag 与给定标签关联的帖子列表,最新的在前
Post 给定帖子的元数据和内容
Author 关于作者的信息和他们所写的帖子列表

您将在下一步中使用动态数据更新这些组件。

AuthorLink组件

您将创建的第一个组件显示指向作者的链接。

AuthorLink.vuesrc/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。相反,其他组件将使用authorprop传入作者信息。

PostList组件

PostList组件接受一个postsprop,其结构对应于你的 GraphQL API 中关于帖子的数据。该组件还接受一个布尔 showAuthor属性,您将false在作者的页面上设置它,因为它是冗余信息。该组件应显示以下功能:

  • 帖子的标题和副标题,将它们链接到帖子页面
  • 指向帖子作者的链接AuthorLink(如果showAuthortrue
  • 发布日期
  • 帖子的元描述
  • 与帖子关联的标签列表

PostList.vuesrc/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组件

您将创建的下一个组件是博客上所有帖子的列表。它需要显示两条信息:

  1. 最近的帖子标题
  2. 帖子列表,使用 PostList

AllPosts.vuesrc/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.vuesrc/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.vuesrc/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.vuesrc/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.vuesrc/目录中打开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以指定httpEndpointwsEndpoint属性:

new Vue({
  ...
  apolloProvider: createProvider({
    httpEndpoint: 'http://localhost:8000/graphql',
    wsEndpoint: null,
  }),
  ...
})

现在您已准备好开始添加查询以填充您的页面。您将通过向created()多个 SFC添加一个函数来实现此目的。created()是一个特殊的Vue 生命周期钩子,当组件即将在页面上呈现时执行。您可以使用此挂钩来查询要呈现的数据,以便在您的组件呈现时可以使用这些数据。您将为以下组件创建查询:

  • Post
  • Author
  • PostsByTag
  • AllPosts

您可以从创建Post查询开始。

Post查询

对单个帖子的查询接受slug所需帖子的 。它应该返回所有必要的信息以显示帖子信息和内容。

您将使用$apollo.queryhelper 和gqlhelper 在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 组件

您涵盖了很多基础知识,因此请尝试确定一些新方法,以便在不同的上下文中使用这些概念来巩固您的学习。快乐编码,快乐写博客!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。