歡迎來到本系列文章的第二階段:動手做、做中學。
只有透過實際操作,我們才能真正掌握知識並內化為自己的硬實力。
在這一階段,我們將一同運用 Vue 3 的 Composition API 和 Vue Apollo,從零開始打造一個部落格 SPA 應用。在此過程中,我們不僅可以學習到在 Vue 框架中開發 GraphQL 的完整 CRUD 流程,還會接觸到從基礎到進階的 GraphQL Operation 技巧。
部落格應用預覽
部落格應用預覽 (RWD)
讓我們一起將學到的理論化為實踐,親眼見證這些魔法在真實世界中的運作!
Day12 開始前分支:feat/day_12/01_blog-layout-design
Day12 進度完成分支:feat/day_12/02_implement_blog_list_view
請參考 [Day09] 召喚伺服器:Mock GraphQL Server 與快速測試 GraphQL 技巧
我們將使用該篇文章內介紹的 GraphQLZero 作為 Fake GraphQL API
從文章列表開始
當使用者瀏覽部落格時,第一個畫面往往決定了他們是否會繼續停留。
部落格文章列表不僅僅是一系列的文章標題,更決定了知識結構內容的組成,是吸引流量的第一道門檻。
部落格文章列表功能主要包含以下元素:
完整的範例程式碼請參考:feat/day_12/02_implement_blog_list_view
在 src/views/BlogListView.vue
中:
getAllPost
graphql-tag
將上述語法嵌入 TypeScript中
graphql-tag
是一個用於解析 GraphQL 查詢字串的 JavaScript 模板文字標籤函數,使用此套件可以確保查詢語法的正確性,並將查詢轉換為用於客戶端或伺服器操作的標準化格式。<script setup lang="ts">
import { computed } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'
import ArticleList from '@/components/articles/ArticleList.vue'
const gqlGetAllPosts = gql`
query getAllPost {
posts {
data {
id
title
body
user {
id
name
email
}
comments {
data {
id
name
email
body
}
}
}
}
}
`
const { result, loading, error } = useQuery(gqlGetAllPosts)
const posts = computed(() => (result.value?.posts?.data ?? []))
</script>
<template>
...
</template>
我們可以使用 Apollo Client DevTool 來驗證串接是否成功:
在前述的文章資料集中,可以看到 useQuery 可以解構出 result
, loading
, error
const { result, loading, error } = useQuery(gqlGetAllPosts)
posts
。處理不同狀態的頁面呈現
在 src/views/BlogListView.vue
中,我們使用 loading
, error
來呈現取得資料集的目前狀態,只有當查詢載入完畢且沒有錯誤,才會使用 ArticleList
顯示文章列表
<template>
<div v-if="loading">
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4" />
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px] mb-2.5" />
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5" />
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5" />
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[300px] mb-2.5" />
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px]" />
<span class="sr-only">Loading...</span>
</div>
</div>
<div v-else-if="error">
<p>Error: {{ error.message }}</p>
</div>
<div v-else>
<ArticleList :posts="posts" />
</div>
</template>
ArticleList
使用 props
傳入的 posts
,並使用 v-for
與 ArticleCard
渲染畫面
<script setup lang="ts">
import ArticleCard from '@/components/articles/ArticleCard.vue'
import type { Post } from '@/types/blogTypes'
const props = defineProps({
posts: {
type: Array as () => Post[],
default: () => [],
},
})
</script>
<template>
<section class="bg-white dark:bg-gray-900">
<div class="grid gap-8 lg:grid-cols-2">
<ArticleCard v-for="post in posts" :key="post.id" :post="post" />
</div>
</section>
</template>
為了模組化與重用,以及程式碼的可讀性,我們使用單獨的元件 ArticleCard
來渲染單個文章項目卡片
ArticleCard
使用 props
傳入的 post
渲染畫面
<script setup lang="ts">
import { toRefs } from 'vue'
import type { Post } from '@/types/blogTypes'
const props = defineProps<{
post: Post
}>()
const { post } = toRefs(props)
</script>
<template>
<article
class="p-6 bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700"
>
...
</article>
</template>
今天我們迅速完成了部落格專案的首個 GraphQL 查詢,並探討了 Vue Apollo 的 useQuery 與 Vue 框架的整合基礎。細心的讀者可能已經發現,有許多地方還有優化的空間,例如,GraphQL Schema 的 TypeScript 定義是否真的只能手動編寫?
在明天的文章中,我們將介紹如何利用 graphql-code-generator
自動生成 GraphQL Schema 的 TypeScript 定義,進一步提升開發效率!