Thứ năm, 12/12/2019 | 00:00 GMT+7

Cách xây dựng blog với Vue, GraphQL và Apollo Client


Hình ảnh hiển thị Biểu trưng Adonis, GraphQL và Apollo Server

Trong bài viết này, bạn sẽ xây dựng một blog sử dụng server GraphQL. Ta sẽ xây dựng ứng dụng blog bằng ứng dụng client Apollo và Vue. Bạn có thể lấy server GraphQL để có thể làm theo hướng dẫn.

Kích hoạt CORS

Server GraphQL được xây dựng bằng AdonisJS. AdonisJS cung cấp một gói mà ta có thể sử dụng để xử lý Chia sẻ tài nguyên nhiều nguồn root (CORS) trên API của ta . Theo mặc định, CORS bị tắt trên AdonisJS, vì vậy ta cần bật nó lên. Để bật CORS trên ứng dụng AdonisJS, ta đặt origin thành true trong config/cors.js như bên dưới:

config / cors.js
origin: true

Mặc dù server GraphQL nhân bản đã được kích hoạt CORS, nhưng điều đáng nói là nó.

Khởi động Server GraphQL

Vì ứng dụng blog của ta sẽ sử dụng server GraphQL, ta cần khởi động server và giữ cho nó chạy trong phần còn lại của hướng dẫn. Để bắt đầu, ta sẽ cd vào folder dự án server GraphQL và chạy lệnh dưới đây:

  • adonis serve --dev

Thao tác này sẽ khởi động server GraphQL và giữ cho nó chạy.

 Server  GraphQL đã bắt đầu và đang chạy

Phần còn lại của hướng dẫn giả sử bạn đã khởi động server GraphQL và nó đang chạy.

Với việc đó, ta hãy bắt đầu xây dựng ứng dụng blog của bạn .

Tạo ứng dụng Vue

Ta sẽ bắt đầu bằng cách tạo một ứng dụng Vue mới bằng Vue CLI:

  • vue init webpack graphql-blog-app
  • // select `Y` to use Vue router

Thao tác này sẽ tạo một ứng dụng Vue mới với tên graphql-blog-app và cài đặt các phần phụ thuộc của nó.

Cài đặt các gói cần thiết

Với ứng dụng đã tạo, ta có thể chuyển sang cài đặt các gói cần thiết để xây dựng ứng dụng blog GraphQL của bạn :

  • cd graphql-blog-app
  • npm install --save vue-apollo@next graphql apollo-client apollo-link apollo-link-context apollo-link-http apollo-cache-inmemory graphql-tag

Hãy nhanh chóng xem qua từng gói:

  • vue - apollo: Tích hợp Apollo / GraphQL cho VueJS. Ta cài đặt version plugin mới nhất cho phép ta sử dụng tất cả các tính năng tuyệt vời đi kèm với Apollo client 2.0.
  • graphql: Một triển khai tham chiếu của GraphQL cho JavaScript.
  • apollo - client: Ứng dụng khách GraphQL bộ nhớ đệm có đầy đủ tính năng, sẵn sàng production cho mọi server hoặc khung giao diện user .
  • apollo - link: Một giao diện tiêu chuẩn để sửa đổi stream điều khiển của các yêu cầu GraphQL và tìm nạp kết quả GraphQL.
  • apollo - link - context: Được sử dụng để đặt ngữ cảnh cho hoạt động của bạn, ngữ cảnh này được sử dụng bởi các liên kết khác trong chuỗi.
  • apollo - link - http: Được sử dụng để nhận kết quả GraphQL qua mạng bằng cách sử dụng tìm nạp HTTP.
  • apollo - cache - inmemory: Triển khai cache cho Apollo Client 2.0.
  • graphql - tag: Một thẻ nghĩa đen của mẫu JavaScript phân tích cú pháp các truy vấn GraphQL.

Cài đặt Vue Apollo

Tiếp theo, hãy đặt các gói để sử dụng. Ta sẽ bắt đầu bằng cách tạo một ApolloClient và cài đặt plugin VueApollo . Mở src/main.js và thêm mã bên dưới vào đó:

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)

Ta tạo một version mới của httpLink với URL ( http://localhost:3333/graphql ) của server GraphQL của ta . Sau đó, ta tạo một ứng dụng client Apollo bằng cách sử dụng httpLink đã tạo ở trên và chỉ định ta muốn có một bộ nhớ đệm trong bộ nhớ. Cuối cùng, ta cài đặt plugin Vue Apollo.

Tiếp theo, hãy tạo một đối tượng apolloProvider mà ta sẽ chỉ định trên thành phần root của ta :

src / main.js

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

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

Ta tạo một version mới của plugin Vue Apollo bằng cách sử dụng apolloClient được tạo làm ứng dụng client mặc định của ta . Cuối cùng, ta sử dụng đối tượng apolloProvider bằng cách thêm nó vào version Vue của ta , giống như cách ta sử dụng bộ định tuyến Vue.

Thêm Bulma

Với mục đích của hướng dẫn này, ta sẽ sử dụng Bulma CSS. Vì vậy, hãy thêm nó vào. Mở index.html và cập nhật như bên dưới:

index.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>

Ta tham khảo Bulma trên CDN.

Xóa mã không sử dụng

Có một số file và mã đi kèm khi ta tạo ứng dụng Vue mà ta sẽ không sử dụng trong hướng dẫn này. Hãy xóa chúng để chúng không ảnh hưởng đến ứng dụng của ta . Xóa thành phần Hello và xóa tất cả các tham chiếu của nó khỏi src/router/index.js .

Thêm bố cục chính

Blog sẽ sử dụng một bố cục chung trên các trang của nó. Trong trường hợp đó, hãy xác định một bố cục mà tất cả các trang sẽ sử dụng. Để thực hiện việc này, hãy mở src/App.vue và cập nhật như sau:

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>

Ta thêm một tiêu đề mà tất cả các trang sẽ sử dụng.

Đăng ký user

User có thể đăng ký ứng dụng blog của ta . Ta sẽ tạo một thành phần SignUp sẽ xử lý điều đó. Vì vậy, trong src/components tạo một folder Admin mới. Tất cả các thành phần liên quan đến administrator sẽ được tạo trong folder này.

Trước khi tạo thành phần SignUp , hãy tạo một file chuyên dụng chứa tất cả các truy vấn và đột biến GraphQL của ta . Ta sẽ tạo file này trực tiếp bên trong src . Tạo file graphql.js bên trong src và dán mã bên dưới vào đó:

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
        }
    }`

Đây là đột biến GraphQL sẽ xử lý việc tạo user mới trên server GraphQL của ta . Nó lấy tên user , email và password của user . Các biến này sẽ được chuyển từ thành phần SignUp .

Tiếp theo, hãy tạo thành phần SignUp . Trong folder Admin , tạo file SignUp.vue và dán mã bên dưới vào đó:

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>

Thành phần này hiển thị một biểu mẫu để user đăng ký. Sau khi biểu mẫu được gửi, một phương thức signup sẽ được gọi. Trong phương pháp signup , ta sử dụng phương thức mutate có sẵn trên this.$apollo Apollo này (từ plugin Vue Apollo). Ta sử dụng đột biến SIGNUP_MUTATION được tạo trước đó và chuyển các biến cần thiết. Khi quá trình đăng ký thành công (nghĩa là user đã được tạo), ta chuyển hướng user đến trang đăng nhập (mà ta sẽ sớm tạo).

Thêm lộ trình đăng ký

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js

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

// add these inside the `routes` array
{
    path: '/signup',
    name: 'SignUp',
    component: SignUp
},

Bây giờ khi ta truy cập vào /signup route, ta sẽ thấy biểu mẫu đăng ký của bạn như trong hình ảnh bên dưới:

Trang blog với trang đăng ký tên  user , email và password

Đăng nhập user

Hãy thêm khả năng cho user đăng nhập. Giống như ta đã làm với user đăng ký, trước tiên hãy tạo đột biến GraphQL. Thêm mã bên dưới vào src/graphql.js :

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

Đột biến GraphQL này xử lý đăng nhập của user vào server GraphQL của ta . Nó lấy email và password của user .

Tiếp theo, trong folder Admin , tạo file LogIn.vue và dán mã bên dưới vào đó:

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>

Thành phần này hiển thị một biểu mẫu đơn giản để user đăng nhập. Sau khi biểu mẫu được gửi, một phương thức login sẽ được gọi. Trong phương thức login , ta sử dụng phương thức mutate . Ta sử dụng đột biến LOGIN_MUTATION đã tạo trước đó và chuyển các biến cần thiết. Sau khi quá trình đăng nhập thành công, ta lưu mã thông báo nhận được từ server GraphQL của ta vào localstorage và chuyển hướng user .

Thêm lộ trình đăng nhập

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js

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

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

Bây giờ khi ta truy cập vào /login route, ta sẽ thấy biểu mẫu đăng nhập của bạn như trong hình dưới đây:

trang đăng nhập chỉ với hộp email và password

Tạo thành phần menu

Trước khi bắt đầu bổ sung phần administrator của blog, hãy tạo một thành phần Menu sẽ phục vụ làm menu chuyển thanh bên. Trong folder Admin , tạo file Menu.vue và dán mã bên dưới vào đó:

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>

Điều này chỉ hiển thị liên kết đến một số phần quản trị của ứng dụng blog ngoài.

Hiển thị User

Trên phần quản trị, ta muốn có thể xem danh sách user đã được tạo. Vì vậy, ta đã tạo thành phần Users . Nhưng trước tiên, hãy viết truy vấn GraphQL sẽ tìm nạp tất cả user đã tạo. Thêm mã bên dưới vào src/graphql.js :

src / graphql.js
export const ALL_USERS_QUERY = gql`query AllUsersQuery {
        allUsers {
            id
            username
            email
        }
    }`

Truy vấn GraphQL này tìm nạp tất cả user từ server GraphQL của ta .

Tiếp theo, hãy tạo thành phần Users . Trong folder Admin , hãy tạo file Users.vue và dán mã bên dưới vào đó:

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>

Ta sử dụng thành phần Menu đã tạo trước đó. Sau đó, ta xác định dữ liệu của ta sẽ được điền khi dữ liệu được nhận từ server GraphQL của ta . Trong đối tượng apollo , ta thêm truy vấn GraphQL của bạn để tìm nạp tất cả user . Điều này sử dụng ALL_USERS_QUERY mà ta đã tạo ở trên. Điều quan trọng cần lưu ý là, tên dữ liệu của ta ( allUsers trong trường hợp này) phải giống với tên được sử dụng trong truy vấn GraphQL của ta ( allUsers trong trường hợp này). Sau khi allUsers được điền dữ liệu từ server GraphQL của ta , ta hiển thị những user trong một bảng bằng cách lặp qua mảng user . Ta cũng thêm một liên kết để xem chi tiết của từng user .

Thêm tuyến user

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js

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

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

Bây giờ khi ta truy cập vào tuyến /admin/users , ta sẽ thấy danh sách user như trong hình dưới đây:

trang blog với danh sách  user  hiện tại

Xem chi tiết user

Trong phần cuối cùng, ta thêm một liên kết để xem chi tiết user . Bây giờ, ta hãy thực hiện nó. Thêm mã bên dưới vào src/graphql.js :

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

Truy vấn GraphQL này tìm nạp user theo ID của họ từ server GraphQL của ta . Nó lấy ID của user làm đối số. ID user sẽ được chuyển từ thành phần UserDetails .

Tiếp theo, hãy tạo thành phần UserDetails . Trong folder Admin , hãy tạo file UserDetails.vue và dán mã bên dưới vào đó:

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>

Ta hiển thị tên user , email và số lượng bài viết đã tạo của user được chỉ định. USER_QUERY chấp nhận ID của user mà ta muốn xem thông tin chi tiết của họ. ID user được lấy từ các thông số tuyến. Nghĩa là , đã cho /admin/users/12 , 12 là ID của một user cụ thể. Ta cần một cách để chuyển ID này vào truy vấn của ta . Để làm điều này, ta sử dụng tham số phản ứng bằng cách xác định một hàm variables trả về một đối tượng chứa ID user .

Thêm tuyến chi tiết user

Mở src/router/index.js và thêm mã bên dưới vào đó. Tuyến đường này phải nằm dưới tất cả các tuyến đường trước đó:

src / router / index.js

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

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

Bây giờ ta có thể xem chi tiết user cụ thể:

trang blog với thông tin chi tiết của  User  được hiển thị

Cho phép user

Chỉ những user được xác thực mới có thể thêm bài mới. Vì vậy, ta cần một cách để chuyển tiêu đề Authorization với mã thông báo user cùng với yêu cầu thêm bài đăng mới để cho biết user có thể thêm bài đăng mới. Với apollo-link-context , ta có thể dễ dàng thực hiện điều này. Mở src/main.js và thêm mã bên dưới vào đó:

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()
})

Đầu tiên, ta nhập apollo-link-context . Sau đó, ta sử dụng nó để tạo authLink lấy mã thông báo của user từ bộ nhớ local và trả về các tiêu đề có chứa tiêu đề Ủy quyền. Cuối cùng, ta sử dụng authLink trong ứng dụng client Apollo của bạn .

Bây giờ, tiêu đề Ủy quyền sẽ được gửi cùng với tất cả các yêu cầu được thực hiện đến server GraphQL của ta .

Thêm bài mới

Bài viết là trung tâm của bất kỳ blog nào. User sẽ có thể thêm một bài viết mới. , trước tiên ta sẽ tạo đột biến GraphQL để thêm bài đăng mới vào blog của ta . Thêm mã bên dưới vào 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
            }
        }
    }`

Đột biến này lấy tiêu đề và nội dung của bài đăng mà ta muốn thêm vào server GraphQL của bạn .

Tiếp theo, tạo một thành phần AddPost trong folder Admin và dán đoạn mã dưới đây vào đó:

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>

Thành phần này hiển thị một biểu mẫu để thêm bài đăng mới. Nó sử dụng ADD_POST_MUTATION chuyển cho nó các biến cần thiết. Vì ứng dụng client Apollo lưu vào bộ nhớ cache (trong bộ nhớ trong trường hợp của ta ) nó truy vấn, ta cần một cách để cập nhật bộ nhớ cache khi nào ta thực hiện các hành động đột biến. Lưu ý có một chức năng update mà ta sử dụng để cập nhật cửa hàng bằng cách thêm bài đăng mới được thêm vào bộ nhớ cache. Đầu tiên, ta tìm nạp dữ liệu từ bộ nhớ cache phù hợp với truy vấn của ta ( ALL_POSTS_QUERY ), sau đó ta thêm bài đăng mới vào mảng allPosts . Cuối cùng, ta ghi dữ liệu mới trở lại bộ nhớ cache. Khi bài viết được thêm thành công, ta chuyển hướng đến danh sách các bài đăng ( ta sẽ tạo ngay sau đó).

Thêm Thêm tuyến bài đăng

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js
import AddPost from '@/components/Admin/AddPost'

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

User có thể thêm bài viết mới ngay bây giờ:

trang blog với màn hình Thêm bài đăng

Hiển thị bài đăng

Trước tiên, ta sẽ tạo truy vấn GraphQL bằng cách thêm mã bên dưới vào src/graphql.js :

src / graphql.js

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

Truy vấn GraphQL này tìm nạp tất cả các bài đăng từ server GraphQL của ta .

Tiếp theo, tạo thành phần Posts trong folder Admin và dán mã bên dưới vào đó:

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>

Ta sử dụng thành phần Menu đã tạo trước đó. Sau đó, ta xác định dữ liệu của ta sẽ được điền khi dữ liệu được nhận từ server GraphQL của ta . Trong đối tượng apollo , ta thêm truy vấn GraphQL của bạn để tìm nạp tất cả user . Điều này sử dụng ALL_USERS_QUERY mà ta đã tạo ở trên. Điều quan trọng cần lưu ý là, tên dữ liệu của ta ( allUsers trong trường hợp này) phải giống với tên được sử dụng trong truy vấn GraphQL của ta ( allUsers trong trường hợp này). Sau khi allUsers được điền dữ liệu từ server GraphQL của ta , ta hiển thị những user trong một bảng bằng cách lặp qua mảng user . Ta cũng thêm một liên kết để xem chi tiết từng user .

Thêm tuyến bài đăng

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js

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

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

Bây giờ khi ta truy cập vào tuyến đường /admin/posts , ta sẽ thấy danh sách các bài đăng như trong hình dưới đây:

Danh sách tiêu đề và tác giả bài viết

Tạo Trang chủ Blog

Trang chủ blog sẽ hiển thị danh sách tất cả các bài viết đã tạo giống như trong phần hiển thị bài viết. Trên thực tế, trang chủ sẽ sử dụng cùng một GraphQL được sử dụng để hiển thị các bài đăng. Nó chỉ là đánh dấu của trang chủ sẽ khác nhau. Tạo một thành phần Home bên trong src/components và thêm mã bên dưới vào nó:

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>

Như ta có thể thấy phần JavaScript giống với phần của thành phần Posts . Chỉ là đánh dấu khác nhau. Ta lặp qua mảng bài đăng và hiển thị tiêu đề của mỗi bài đăng được liên kết với slug của chúng.

Thêm tuyến đường về nhà

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js
import Home from '@/components/Home'

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

Truy cập / route, ta sẽ thấy trang chủ blog của ta như trong hình dưới đây:

Trang chủ blog hiển thị các bài viết mới nhất

Xem bài đăng

Điều cuối cùng cần thêm là khả năng xem một bài đăng cụ thể. Thêm mã bên dưới vào 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
            }
        }
    }`

Truy vấn này tìm nạp một bài đăng bằng slug của nó. Nó lấy slug của bài đăng để được tìm nạp làm đối số.

Tiếp theo, tạo một thành phần SinglePost bên trong src/components và thêm mã bên dưới vào nó:

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>

Ta chỉ hiển thị tiêu đề bài đăng và nội dung của nó, sau đó là một liên kết để quay lại trang chủ. Phần JavaScript theo sau việc triển khai được sử dụng trong việc hiển thị chi tiết user . Trong trường hợp này, ta lấy post slug từ các tham số tuyến.

Thêm Xem tuyến bài đăng

Mở src/router/index.js và thêm mã bên dưới vào nó:

src / router / index.js

import SinglePost from '@/components/SinglePost'

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

Lưu ý: Tuyến đường này phải là tuyến đường cuối cùng trong mảng các tuyến đường.

Bây giờ ta có thể xem một bài đăng:

Văn bản ví dụ về chế độ xem một bài đăng "Xin chào Adonis"

Kết luận

Trong hướng dẫn này, ta đã xem cách tạo một ứng dụng blog với GraphQL, Apollo client và VueJS. Ta cũng đã biết cách kết nối ứng dụng giao diện user của bạn với server GraphQL. Mã hoàn chỉnh cho hướng dẫn này có sẵn trên GitHub .


Tags:

Các tin liên quan