Cách xây dựng blog với Vue, GraphQL và Apollo Client
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:
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.
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 đó:
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 :
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:
<!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:
<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 đó:
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 đó:
<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ó:
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:
Đă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
:
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 đó:
<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ó:
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:
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 đó:
<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
:
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 đó:
<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ó:
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:
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
:
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 đó:
<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 đó:
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ể:
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 đó:
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
:
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 đó:
<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ó:
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ờ:
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
:
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 đó:
<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ó:
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:
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ó:
<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ó:
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:
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
:
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ó:
<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ó:
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:
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 .
Các tin liên quan