如何使用Vue、GraphQL和Apollo客户端构建博客

来自菜鸟教程
跳转至:导航、​搜索

介绍

在本文中,您将构建一个使用 GraphQL 服务器的博客。 我们将使用 Apollo 客户端和 Vue 构建博客应用程序。

先决条件

要完成本教程,您需要:

  • Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
  • MySQL在本地安装并运行,您可以按照如何安装MySQL来完成。

本教程假设您了解 JavaScript 并熟悉 Vue 和 GraphQL。

本教程已使用 Node v14.4.0、npm v6.14.5、MySQL v14.14、@adonisjs/cli v4.0.12、@vue/cli v4.4.6、[X120X ] v2.5.2、graphql v15.1.0 和 apollo-client v2.6.10。

创建 GraphQL 服务器

您可以获取 GraphQL 服务器,以便您可以按照教程进行操作。

克隆存储库后,导航到 GraphQL 服务器项目目录:

cd adonis-graphql-server

安装所需的软件包:

npm install

.env.example 复制到 .env

cp .env.example .env

根据需要编辑 .env 文件,以便数据库信息为您正在运行的 MySQL 数据库提供正确的凭据。 DB_USERDB_PASSWORD 可能需要更改。

为 Adonis 生成密钥:

npx @adonisjs/cli key:generate

迁移数据库配置:

npx @adonisjs/cli migration:run

启用 CORS

GraphQL 服务器是用 AdonisJS 构建的。 AdonisJS 提供了一个包,我们可以使用它来处理我们 API 上的跨域资源共享 (CORS)。 默认情况下,AdonisJS 上的 CORS 是关闭的,所以我们需要启用它。

为了在 AdonisJS 应用上启用 CORS,我们在 config/cors.js 中将 origin 设置为 true,如下所示:

配置/cors.js

origin: true

克隆的 GraphQL 服务器已经启用了 CORS,但值得一提的是。

启动 GraphQL 服务器

由于我们的博客应用程序将使用 GraphQL 服务器,因此我们需要启动服务器并使其在本教程的其余部分保持运行。

要开始,请确保您位于 GraphQL 服务器项目目录中并运行以下命令:

npx @adonisjs/cli serve --dev

这将启动 GraphQL 服务器并保持运行。

本教程的其余部分假设您已经启动了 GraphQL 服务器并且它正在运行。

处理完这些,让我们开始构建我们的博客应用程序。

第 1 步——创建一个 Vue 应用程序

我们将首先使用 Vue CLI 创建一个新的 Vue 应用程序:

npx -p @vue/cli -p @vue/cli-init vue init webpack graphql-blog-app

注意: 现代 Vue 项目可以利用:

npx @vue/cli create graphql-blog-app

系统将提示您有关您的项目的问题。 以下是所做的一些选择。 对于本教程,安装 vue-router 很重要:

? Project name graphql-blog-app
? Project description A Vue.js project
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm

这将创建一个名为 graphql-blog-app 的新 Vue 应用程序并安装其依赖项。

导航到新创建的目录:

cd graphql-blog-app

通过在终端中运行以下命令,可以随时在浏览器中运行和查看该应用程序:

npm start

第 2 步 — 安装软件包

创建应用程序后,我们可以继续安装必要的包来构建我们的 GraphQL 博客应用程序:

npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-context apollo-link-http apollo-cache-inmemory graphql-tag

让我们快速浏览每个包:

  • vue-apollo:VueJS 的 Apollo/GraphQL 集成。 我们安装了最新版本的插件,允许我们使用 Apollo 客户端 2.0 附带的所有强大功能。
  • graphql:GraphQL for JavaScript 的参考实现。
  • apollo-client:一个功能齐全、生产就绪的缓存 GraphQL 客户端,适用于每个服务器或 UI 框架。
  • apollo-link:用于修改 GraphQL 请求的控制流和获取 GraphQL 结果的标准接口。
  • apollo-link-context:用于为您的操作设置上下文,供后续链中的其他链接使用。
  • apollo-link-http:用于使用 HTTP fetch 通过网络获取 GraphQL 结果。
  • apollo-cache-inmemory:Apollo Client 2.0 的缓存实现。
  • graphql-tag:解析 GraphQL 查询的 JavaScript 模板文字标签。

第 3 步 — 设置 Vue Apollo

接下来,让我们使用这些包。 我们将首先创建一个 ApolloClient 实例并安装 VueApollo 插件。 打开 src/main.js 并添加以下代码:

src/main.js

// ...

import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'

const httpLink = new HttpLink({
    // URL to graphql server, you should use an absolute URL here
    uri: 'http://localhost:3333/graphql'
})

// create the apollo client
const apolloClient = new ApolloClient({
    link: httpLink,
    cache: new InMemoryCache()
})

// install the vue plugin
Vue.use(VueApollo)

我们使用 GraphQL 服务器的 URL (http://localhost:3333/graphql) 创建一个新的 httpLink 实例。 然后我们使用上面创建的 httpLink 创建一个 Apollo 客户端,并指定我们想要一个内存缓存。 最后,我们安装 Vue Apollo 插件。

接下来,让我们创建一个我们将在根组件上指定的 apolloProvider 对象:

src/main.js

// ...

const apolloProvider = new VueApollo({
    defaultClient: apolloClient
})

// update Vue instance by adding `apolloProvider`
/* eslint-disable no-new */
new Vue({
    el: '#app',
    router,
    apolloProvider,
    template: '<App/>',
    components: { App }
})

我们使用默认客户端创建的 apolloClient 创建 Vue Apollo 插件的新实例。 最后,我们通过将 apolloProvider 对象添加到我们的 Vue 实例中来使用它,就像我们使用 Vue 路由器一样。

第 4 步 - 添加布尔玛

出于本教程的目的,我们将使用 Bulma CSS。 所以,让我们添加它。 打开index.html更新如下:

索引.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>GraphQL Blog App</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.min.css">
</head>
<body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
</body>
</html>

我们在这个内容交付网络 (CDN) 上引用了 Bulma。

第 5 步 — 删除未使用的代码

我们在创建 Vue 应用程序时附带了一些文件和代码,我们不会在本教程中使用它们。 让我们删除它们,这样它们就不会干扰我们的应用程序。

删除 HelloWorld 组件并从 src/router/index.js 中删除其所有引用。

第 6 步 - 创建主布局

该博客将在其页面中使用通用布局。 在这种情况下,让我们定义一个所有页面都将使用的布局。 为此,请打开 src/App.vue 并更新如下:

src/App.vue

<template>
    <div id="app">
        <nav class="navbar is-primary" role="navigation" aria-label="main navigation">
            <div class="container">
                <div class="navbar-brand">
                    <router-link class="navbar-item" to="/">Blog App</router-link>

                    <button class="button navbar-burger">
                        <span></span>
                        <span></span>
                        <span></span>
                    </button>
                </div>
            </div>
        </nav>
        <router-view/>
    </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

我们添加一个所有页面都将使用的标题。

第 7 步 - 创建用户注册页面

用户应该能够注册我们的博客应用程序。 我们将创建一个 SignUp 组件来处理它。 因此,在 src/components 中创建一个新的 Admin 文件夹。 所有与管理员相关的组件都将在此文件夹内创建。

在我们创建 SignUp 组件之前,让我们创建一个专用文件来保存我们所有的 GraphQL 查询和突变。 我们将直接在 src 中创建这个文件。 在 src 中创建一个 graphql.js 文件并将以下代码粘贴到其中:

src/graphql.js

import gql from 'graphql-tag'

export const SIGNUP_MUTATION = gql`
  mutation SignupMutation($username: String!, $email: String!, $password: String!) {
    createUser(
      username: $username,
      email: $email,
      password: $password
    ) {
      id
      username
      email
    }
  }
`

这是 GraphQL 突变,它将处理在我们的 GraphQL 服务器上创建新用户。 它需要用户的用户名、电子邮件和密码。 这些变量将从 SignUp 组件传递。

接下来,让我们创建 SignUp 组件。 在 Admin 文件夹中,创建一个 SignUp.vue 文件并将以下代码粘贴到其中:

src/components/Admin/SignUp.vue

<template>
   <section class="section">
    <div class="columns">
      <div class="column is-4 is-offset-4">

        <h2 class="title has-text-centered">Signup</h2>

        <form method="POST" @submit.prevent="signup">
          <div class="field">
            <label class="label">Username</label>
            <p class="control">
              <input
                type="text"
                class="input"
                v-model="username"
              />
            </p>
          </div>

          <div class="field">
            <label class="label">E-Mail Address</label>
            <p class="control">
              <input
                type="email"
                class="input"
                v-model="email"
              />
            </p>
          </div>

          <div class="field">
            <label class="label">Password</label>
            <p class="control">
              <input
                type="password"
                class="input"
                v-model="password"
              />
            </p>
          </div>

          <p class="control">
            <button class="button is-primary is-fullwidth is-uppercase">SignUp</button>
          </p>
        </form>
      </div>
    </div>
   </section>
</template>

<script>
import { SIGNUP_MUTATION } from '@/graphql'

export default {
  name: 'SignUp',
  data () {
    return {
      username: '',
      email: '',
      password: ''
    }
  },
  methods: {
    signup () {
      this.$apollo
        .mutate({
          mutation: SIGNUP_MUTATION,
          variables: {
            username: this.username,
            email: this.email,
            password: this.password
          }
        })
        .then(response => {
          // redirect to login page
          this.$router.replace('/login')
        })
    }
  }
}
</script>

该组件呈现一个表单供用户注册。 提交表单后,将调用 signup 方法。 在 signup 方法中,我们使用 this.$apollo 上可用的 mutate 方法(来自 Vue Apollo 插件)。 我们使用之前创建的 SIGNUP_MUTATION 突变并传递必要的变量。 一旦注册过程成功(即用户已创建),我们将用户重定向到登录页面(我们将很快创建)。

添加注册路线

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import SignUp from '@/components/Admin/SignUp'

// ...

export default new Router({
  routes: [
    // add these inside the `routes` array
    {
      path: '/signup',
      name: 'SignUp',
      component: SignUp
    }
  ]
})

现在,当我们访问 /signup 路由时,我们应该会看到如下图所示的注册表单:

第 8 步 - 创建用户登录页面

让我们添加用户登录的功能。 就像我们对用户注册所做的那样,让我们首先创建 GraphQL 突变。 将以下代码添加到 src/graphql.js

src/graphql.js

export const LOGIN_MUTATION = gql`
  mutation LoginMutation($email: String!, $password: String!) {
    login(
      email: $email,
      password: $password
    )
  }
`

这个 GraphQL 突变处理用户登录到我们的 GraphQL 服务器。 它需要用户的电子邮件和密码。

接下来,在 Admin 文件夹中,创建一个 LogIn.vue 文件并将以下代码粘贴到其中:

src/components/Admin/LogIn.vue

<template>
   <section class="section">
    <div class="columns">
      <div class="column is-4 is-offset-4">

        <h2 class="title has-text-centered">Login</h2>

        <form method="POST" @submit.prevent="login">
          <div class="field">
            <label class="label">E-Mail Address</label>

            <p class="control">
              <input
                type="email"
                class="input"
                v-model="email"
              />
            </p>
          </div>

          <div class="field">
            <label class="label">Password</label>

            <p class="control">
              <input
                type="password"
                class="input"
                v-model="password"
              />
            </p>
          </div>

          <p class="control">
            <button class="button is-primary is-fullwidth is-uppercase">Login</button>
          </p>
        </form>
      </div>
    </div>
   </section>
</template>

<script>
import { LOGIN_MUTATION } from '@/graphql'

export default {
  name: 'LogIn',
  data () {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    login () {
      this.$apollo
        .mutate({
          mutation: LOGIN_MUTATION,
          variables: {
            email: this.email,
            password: this.password
          }
        })
        .then(response => {
          // save user token to localstorage
          localStorage.setItem('blog-app-token', response.data.login)

          // redirect user
          this.$router.replace('/admin/posts')
        })
    }
  }
}
</script>

该组件呈现一个简单的表单供用户登录。 提交表单后,将调用 login 方法。 在 login 方法中,我们使用了 mutate 方法。 我们使用之前创建的 LOGIN_MUTATION 突变并传递必要的变量。 一旦登录过程成功,我们将从我们的 GraphQL 服务器获取的令牌保存到 localStorage 并重定向用户。

添加登录路由

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import LogIn from '@/components/Admin/LogIn'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/login',
      name: 'LogIn',
      component: LogIn
    }
  ]
})

现在当我们访问 /login 路由时,我们应该看到我们的登录表单,如下图所示:

第 9 步 - 创建菜单组件

在我们开始充实博客的管理部分之前,让我们创建一个 Menu 组件,它将用作侧边栏导航菜单。 在 Admin 文件夹中,创建一个 Menu.vue 文件并将以下代码粘贴到其中:

src/components/Admin/Menu.vue

<template>
  <aside class="menu">
    <p class="menu-label">Post</p>
      <ul class="menu-list">
        <li>
          <router-link to="/admin/posts/new">New Post</router-link>
        </li>
        <li>
          <router-link to="/admin/posts">Posts</router-link>
        </li>
      </ul>
    <p class="menu-label">User</p>
    <ul class="menu-list">
      <li>
        <router-link to="/admin/users">Users</router-link>
      </li>
    </ul>
  </aside>
</template>

这会呈现指向我们博客应用程序的一些管理部分的链接。

第 10 步 — 创建用户列表页面

在管理部分,我们希望能够看到已创建的用户列表。 为此,我们创建了一个 Users 组件。 但首先,让我们编写 GraphQL 查询来获取所有创建的用户。 将以下代码添加到 src/graphql.js

src/graphql.js

export const ALL_USERS_QUERY = gql`
  query AllUsersQuery {
    allUsers {
      id
      username
      email
    }
  }
`

这个 GraphQL 查询从我们的 GraphQL 服务器获取所有用户。

接下来,让我们创建 Users 组件。 在 Admin 文件夹中,创建一个 Users.vue 文件并将以下代码粘贴到其中:

src/components/Admin/Users.vue

<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-3">
          <Menu/>
        </div>
        <div class="column is-9">
          <h2 class="title">Users</h2>

          <table class="table is-striped is-narrow is-hoverable is-fullwidth">
            <thead>
              <tr>
                <th>Username</th>
                <th>Email</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              <tr
                v-for="user in allUsers"
                :key="user.id">
                <td>{{ user.username }}</td>
                <td>{{ user.email }}</td>
                <td>
                  <router-link :to="`/admin/users/${user.id}`">View</router-link>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Menu from '@/components/Admin/Menu'
import { ALL_USERS_QUERY } from '@/graphql'

export default {
  name: 'Users',
  components: {
    Menu
  },
  data () {
    return {
      allUsers: []
    }
  },
  apollo: {
    // fetch all users
    allUsers: {
      query: ALL_USERS_QUERY
    }
  }
}
</script>

我们使用之前创建的 Menu 组件。 然后我们定义我们的数据,一旦从我们的 GraphQL 服务器获取数据,这些数据就会被填充。 在 apollo 对象中,我们添加 GraphQL 查询以获取所有用户。 这利用了我们在上面创建的 ALL_USERS_QUERY。 需要注意的是,我们的数据名称(本例中为 allUsers)必须与 GraphQL 查询中使用的名称相同(本例中为 allUsers)。 一旦 allUsers 填充了来自我们 GraphQL 服务器的数据,我们就会通过遍历用户数组在表格中显示用户。 我们还添加了一个链接来查看每个用户的详细信息。

添加用户路由

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import Users from '@/components/Admin/Users'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/admin/users',
      name: 'Users',
      component: Users
    }
  ]
})

现在,当我们访问 /admin/users 路由时,我们应该会看到如下图所示的用户列表:

第 11 步 - 创建用户详细信息页面

在最后一节中,我们添加了一个链接来查看用户详细信息。 现在,让我们实现它。 将以下代码添加到 src/graphql.js

src/graphql.js

export const USER_QUERY = gql`
  query UserQuery($id: Int!) {
    user(id: $id) {
      id
      username
      email
      posts {
        id
      }
    }
  }
`

这个 GraphQL 查询通过用户的 ID 从我们的 GraphQL 服务器获取用户。 它将用户的 ID 作为参数。 用户 ID 将从 UserDetails 组件传递。

接下来,让我们创建 UserDetails 组件。 在 Admin 文件夹中,创建一个 UserDetails.vue 文件并将以下代码粘贴到其中:

src/components/Admin/UserDetails.vue

<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-3">
          <Menu/>
        </div>
        <div class="column is-9">
          <h2 class="title">User Details</h2>

          <div class="field is-horizontal">
            <div class="field-label is-normal">
              <label class="label">Username</label>
            </div>
            <div class="field-body">
              <div class="field">
                <p class="control">
                  <input class="input is-static" :value="user.username" readonly />
                </p>
              </div>
            </div>
          </div>

          <div class="field is-horizontal">
            <div class="field-label is-normal">
              <label class="label">Email Address</label>
            </div>
            <div class="field-body">
              <div class="field">
                <p class="control">
                  <input class="input is-static" :value="user.email" readonly />
                </p>
              </div>
            </div>
          </div>

          <div class="field is-horizontal">
            <div class="field-label is-normal">
              <label class="label">Number of posts</label>
            </div>
            <div class="field-body">
              <div class="field">
                <p class="control">
                  <input class="input is-static" :value="user.posts.length" readonly />
                </p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Menu from '@/components/Admin/Menu'
import { USER_QUERY } from '@/graphql'

export default {
  name: 'UserDetails',
  components: {
    Menu
  },
  data () {
    return {
      user: '',
      id: this.$route.params.id
    }
  },
  apollo: {
    // fetch user by ID
    user: {
      query: USER_QUERY,
      variables () {
        return {
          id: this.id
        }
      }
    }
  }
}
</script>

我们显示指定用户的用户名、电子邮件和创建的帖子数。 USER_QUERY 接受我们要查看其详细信息的用户的 ID。 用户 ID 是从路由参数中获取的。 也就是说,给定 /admin/users/12,12 是特定用户的 ID。 我们需要一种方法将此 ID 传递给我们的查询。 为此,我们通过定义返回包含用户 ID 的对象的 variables 函数来使用 反应参数

添加用户详细信息路由

打开src/router/index.js,添加下面的代码。 这条路线应该低于所有以前的路线:

src/路由器/index.js

// ...

import UserDetails from '@/components/Admin/UserDetails'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/admin/users/:id',
      name: 'UserDetails',
      component: UserDetails,
      props: true
    }
  ]
})

我们现在应该能够查看特定的用户详细信息:

第 12 步 — 授权用户

只有经过身份验证的用户才能添加新帖子。 因此,我们需要一种方法来传递带有用户令牌的 Authorization 标头以及添加新帖子的请求,这将表示用户实际上可以添加新帖子。 使用 apollo-link-context,我们可以轻松做到这一点。 打开 src/main.js 并添加以下代码:

src/main.js

// ...

import { setContext } from 'apollo-link-context'

// ...

const authLink = setContext((_, { headers }) => {
  // get the authentication token from localstorage if it exists
  const token = localStorage.getItem('blog-app-token')

  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : null
    }
  }
})

// ...

// update apollo client as below
const apolloClient = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
})

首先,我们导入 apollo-link-context。 然后我们利用它创建一个 authLink 从本地存储中获取用户令牌并返回包含授权标头的标头。 最后,我们在 Apollo 客户端中使用了 authLink

现在,授权标头将与向我们的 GraphQL 服务器发出的所有请求一起发送。

第 13 步 - 创建新帖子页面

帖子是任何博客的核心。 用户应该能够添加新帖子。 同样,我们将首先创建 GraphQL 突变以向我们的博客添加新帖子。 将以下代码添加到 src/graphql.js

src/graphql.js

export const ADD_POST_MUTATION = gql`
  mutation AddPostMutation($title: String!, $content: String!) {
    addPost(
      title: $title,
      content: $content
    ) {
      id
      slug
      title
      content
      user {
        id
        username
        email
      }
    }
  }
`

这种突变采用我们想要添加到 GraphQL 服务器的帖子的标题和内容。

接下来,在 Admin 文件夹中创建一个 AddPost 组件并将以下代码粘贴到其中:

src/components/Admin/AddPost.vue

<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-3">
          <Menu/>
        </div>
        <div class="column is-9">
          <h2 class="title">Add Post</h2>

          <form method="post" @submit.prevent="addPost">
            <div class="field">
              <label class="label">Title</label>

              <p class="control">
                <input
                  class="input"
                  v-model="title"
                  placeholder="Post title"
                />
              </p>
            </div>

            <div class="field">
              <label class="label">Content</label>

              <p class="control">
                <textarea
                  class="textarea"
                  rows="10"
                  v-model="content"
                  placeholder="Post content"
                  ></textarea>
              </p>
            </div>

            <p class="control">
              <button class="button is-primary">Add Post</button>
            </p>
          </form>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Menu from '@/components/Admin/Menu'
import { ADD_POST_MUTATION, ALL_POSTS_QUERY } from '@/graphql'

export default {
  name: 'AddPost',
  components: {
    Menu
  },
  data () {
    return {
      title: '',
      content: ''
    }
  },
  methods: {
    addPost () {
      this.$apollo
        .mutate({
          mutation: ADD_POST_MUTATION,
          variables: {
            title: this.title,
            content: this.content
          },
          update: (store, { data: { addPost } }) => {
            // read data from cache for this query
            const data = store.readQuery({ query: ALL_POSTS_QUERY })

            // add new post from the mutation to existing posts
            data.allPosts.push(addPost)

            // write data back to the cache
            store.writeQuery({ query: ALL_POSTS_QUERY, data })
          }
        })
        .then(response => {
          // redirect to all posts
          this.$router.replace('/admin/posts')
        })
    }
  }
}
</script>

该组件呈现一个用于添加新帖子的表单。 它使用 ADD_POST_MUTATION 传递给它必要的变量。 由于 Apollo 客户端缓存(在我们的例子中是在内存中)它查询,所以我们需要一种方法来在我们执行突变操作时更新缓存。 请注意,有一个 update 函数,我们通过将新添加的帖子添加到缓存中来更新商店。 首先,我们从缓存中获取与查询匹配的数据 (ALL_POSTS_QUERY),然后将新帖子添加到 allPosts 数组中。 最后,我们将新数据写回缓存。 成功添加帖子后,我们将重定向到帖子列表(我们将很快创建)。

添加 Add Post 路由

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import AddPost from '@/components/Admin/AddPost'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/admin/posts/new',
      name: 'AddPost',
      component: AddPost
    }
  ]
})

用户现在应该可以添加新帖子了:

第 14 步 - 显示所有帖子

我们将首先通过将以下代码添加到 src/graphql.js 来创建 GraphQL 查询:

src/graphql.js

export const ALL_POSTS_QUERY = gql`
  query AllPostsQuery {
    allPosts {
      id
      title
      slug
      user {
        username
      }
    }
  }
`

这个 GraphQL 查询从我们的 GraphQL 服务器获取所有帖子。

接下来,在 Admin 文件夹中创建一个 Posts 组件,并将以下代码粘贴到其中:

src/components/Admin/Posts.vue

<template>
  <section class="section">
    <div class="container">
      <div class="columns">
        <div class="column is-3">
          <Menu/>
        </div>
        <div class="column is-9">
          <h2 class="title">Posts</h2>

          <table class="table is-striped is-narrow is-hoverable is-fullwidth">
            <thead>
              <tr>
                <th>Title</th>
                <th>User</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              <tr
                v-for="post in allPosts"
                :key="post.id"
              >
                <td>{{ post.title }}</td>
                <td>{{ post.user.username }}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Menu from '@/components/Admin/Menu'
import { ALL_POSTS_QUERY } from '@/graphql'

export default {
  name: 'Posts',
  components: {
    Menu
  },
  data () {
    return {
      allPosts: []
    }
  },
  apollo: {
    // fetch all posts
    allPosts: {
      query: ALL_POSTS_QUERY
    }
  }
}
</script>

我们使用之前创建的 Menu 组件。 然后我们定义我们的数据,一旦从我们的 GraphQL 服务器获取数据,这些数据就会被填充。 在 apollo 对象中,我们添加 GraphQL 查询以获取所有用户。 这利用了我们在上面创建的 ALL_USERS_QUERY。 需要注意的是,我们的数据名称(本例中为 allUsers)必须与 GraphQL 查询中使用的名称相同(本例中为 allUsers)。 一旦 allUsers 填充了来自我们 GraphQL 服务器的数据,我们就会通过遍历用户数组在表格中显示用户。 我们还添加了一个链接来查看每个用户的详细信息。

添加帖子路线

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import Posts from '@/components/Admin/Posts'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/admin/posts',
      name: 'Posts',
      component: Posts
    }
  ]
})

现在,当我们访问 /admin/posts 路由时,我们应该会看到如下图所示的帖子列表:

第 15 步 — 创建博客主页

博客主页将显示创建的所有帖子的列表,就像在显示帖子部分中一样。 事实上,主页将使用与显示帖子完全相同的 GraphQL。 只有主页的标记会有所不同。 在 src/components 内部创建一个 Home 组件,并在其中添加以下代码:

src/components/Home.vue

<template>
  <section class="section">
    <div class="columns">
      <div class="column is-6 is-offset-3">
        <h1 class="title">Latest Posts</h1>

        <h3
          v-for="post in allPosts"
          :key="post.id"
          class="title is-5"
        >
          <router-link :to="post.slug">
            {{ post.title }}
          </router-link>
        </h3>
      </div>
    </div>
  </section>
</template>

<script>
import { ALL_POSTS_QUERY } from '@/graphql'

export default {
  name: 'Home',
  data () {
    return {
      allPosts: []
    }
  },
  apollo: {
    // fetch all posts
    allPosts: {
      query: ALL_POSTS_QUERY
    }
  }
}
</script>

正如我们所见,JavaScript 部分与 Posts 组件的部分相同。 只是不同的标记。 我们循环遍历帖子数组并显示与其 slug 链接的每个帖子的标题。

添加主路由

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import Home from '@/components/Home'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/',
      name: 'Home',
      component: Home
    }
  ]
})

访问 / 路由,我们应该看到我们的博客主页,如下图所示:

第 16 步 - 创建单个帖子页面

最后要添加的是查看特定帖子的能力。 将以下代码添加到 src/graphql.js

src/graphql.js

export const POST_QUERY = gql`
  query PostQuery($slug: String!) {
    post(slug: $slug) {
      id
      title
      slug
      content
      user {
        id
        username
        email
      }
    }
  }
`

此查询通过其 slug 获取帖子。 它需要获取帖子的 slug 作为参数。

接下来,在 src/components 内部创建一个 SinglePost 组件,并在其中添加以下代码:

src/components/SinglePost.vue

<template>
  <section class="section">
    <div class="columns">
      <div class="column is-6 is-offset-3">
        <router-link class="button is-link is-small" to="/">Back Home</router-link>

        <h1 class="title">
          {{ post.title }}
        </h1>

        <div class="content">
          {{ post.content }}
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import { POST_QUERY } from '@/graphql'

export default {
  name: 'SinglePost',
  data () {
    return {
      post: '',
      slug: this.$route.params.slug
    }
  },
  apollo: {
    // fetch post by slug
    post: {
      query: POST_QUERY,
      variables () {
        return {
          slug: this.slug
        }
      }
    }
  }
}
</script>

我们显示帖子标题及其内容以及返回主页的链接。 JavaScript 部分遵循用于显示用户详细信息的实现。 在这种情况下,我们从路由参数中获取 post slug。

添加查看帖子路线

打开src/router/index.js,添加如下代码:

src/路由器/index.js

// ...

import SinglePost from '@/components/SinglePost'

// ...

export default new Router({
  routes: [
    // ...

    // add these inside the `routes` array
    {
      path: '/:slug',
      name: 'SinglePost',
      component: SinglePost,
      props: true
    }
  ]
})

注意:这条路线应该是路线数组中的最后一条路线。


我们现在应该可以查看单个帖子:

结论

在本教程中,我们了解了如何使用 GraphQL、Apollo 客户端和 VueJS 构建博客应用程序。 我们还看到了如何将我们的前端应用程序连接到 GraphQL 服务器。

本教程的 完整代码可在 GitHub 上找到。