Bài viết này sẽ hướng dẫn các bạn các bước để tạo minimal blog với Nuxt JS và WordPress API.
Thông thường, các trang web sử dụng CMS WordPress sẽ cung cấp các điểm cuối API cho các loại dữ liệu WordPress. Cho phép các nhà phát triển tương tác với các trang web từ xa bằng cách gửi và nhận các đối tượng JSON.
Endpoints của một trang web sử dụng WordPress thường sẽ như thế này: http://yoursite.com/wp-json/
Điều kiện
- Trang web sử dụng WordPress framework và phải mở endpoints REST API.
- Có kiến thức cơ bản về VueJS
- Đọc kỹ document của Nuxt JS
Tạo project Nuxt JS
Để tạo project Nuxt Js bạn có thể xem kỹ document để hiểu rõ hơn, hoặc bắt đầu nhanh với create-nuxt-app
:
npx create-nuxt-app <project-name>
hoặcyarn create nuxt-app <project-name>
Mình sẽ tạo project và đặt tên là nuxtjs-minimal-blog
. Trong quá trình tạo project nó sẽ hiển thị một số tùy chọn cho bạn, bạn có thể custom project của mình theo ý muốn.
Cấu trúc thư mục của project khi tạo bằng create-nuxt-app
sẽ trông như thế này:
Để hiểu rõ hơn về cấu trúc thư mục bạn có thể xem tại đây.
Trường hợp nếu bạn muốn sử dụng sass/scss
trong project, bạn phải cài đặt node-sass
và sass-loader
vào package của mình.
yarn add --dev node-sass sass-loader
Custom Layout và cấu trúc của project
Mình tạo thêm component Header và Footer, sau đó custom lại layout default 1 chút. Để custom layout bạn mở file layouts/default.vue để edit.
<template>
<div id="app">
<Header />
<div id="main" class="container">
<nuxt />
</div>
<Footer />
</div>
</template>
<script>
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
export default {
name: 'App',
components: {
Header,
Footer
}
}
</script>
Add thêm css global vào project Nuxt JS
Để thêm css global vào project Nuxt Js, bạn tạo mới file css hoặc scss vào folder assets
, sau đó add đường dẫn file vừa tạo vào options css trong file nuxt.config.js
.
Ở đây mình có tạo một file đặt tên là assets/main.scss
, tiếp theo mình add main.scss vào file nuxt config:
/* ** Global CSS */ css: ['~/assets/main.scss'], /* ** ============ */
Giao diện sau khi custom sẽ trông như thế này
Cấu trúc blog hiện tại như sau:
- Home và About: (Static Routes) có thể coi là 2 page tĩnh, viết content gì vào đó thì tùy bạn
- Blog: (Static Routes) trang blog sẽ list ra các bài viết được get từ api và bao gồm phân trang ở dưới cùng. Khi click vào bài viết sẽ hiển thị nội dung của bài viết đó.
- Single Post: (Dynamic Routes) Single post có cấu trúc như sau http://domain.com/p/slug-post
Dưới đây là biểu thị cho cấu trúc của blog hiên tại, bạn có thể xem thêm chi tiết về Routes Nuxt JS tại đây
pages/ --| p/ -----| _slug.vue --| about.vue --| blog.vue --| index.vue
Đối với Vue JS thì bạn sẽ phải viết routes cho project, nhưng đối với Nuxt JS có ưu điểm ở chỗ nó sẽ tự tạo ra routes cho bạn. Chỉ cần bạn cấu trúc file và folder hợp lý trong project là được.
REST API WordPress
Đây chính là bước quan trọng nhất của bài viết này. 😀
Đầu tiên sẽ kiếm một trang web mà bạn muốn get data, ở đây mình lấy của blog techtalk.vn – chuyên trang về công nghệ của việt nam.
Endpoint của techtalk như sau: https://techtalk.vn/wp-json/wp/v2/posts bạn muốn bao gồm thêm feature image thì hãy thêm tham số này vào ?_embed.
Endpoint đầy đủ mình sử dụng trong project như sau: https://techtalk.vn/wp-json/wp/v2/posts?_embed&page=${page}&per_page=20
Ở endpoint trên sẽ có 1 tham số page được truyền vào để get theo page và mỗi lấy 20 post mỗi page. Hiện tại techtalk có khoảng 5117 bài viết, tương đương khoảng 256 page.
Ở Nuxt JS khi rest api ta sẽ gọi ở trong function asyncData() , khi gọi ở function này, nó sẽ gộp default data và data được lấy về, sau đó tiến hành render.
Bạn có thể xem chi tiết ở trong project đính kèm phía dưới bài viết.
Đây là function asyncData()
asyncData({ $axios, query, error }) { const page = +query.page || 1 return $axios .get( `https://techtalk.vn/wp-json/wp/v2/posts?_embed&page=${page}&per_page=20` ) .then((res) => { console.log(res) console.log(+res.headers['x-wp-total']) return { posts: res.data, totalPosts: +res.headers['x-wp-total'], totalPages: +res.headers['x-wp-totalpages'], currentPage: +page, title: 'Result page ' + +page + ' of ' + +res.headers['x-wp-totalpages'] + ' for Blog' } }) .catch((e) => { console.log('request failed', e) error({ statusCode: 404, message: e }) }) }
Hiển thị chi tiết bài viết
Khi click vào link của bài viết, nó sẽ dẫn người dùng tới bài viết chi tiết, ở đây bạn sẽ cần sử dụng NuxtLink để điều hướng.
<NuxtLink
class="__title"
:to="{
name: 'p-slug',
params: { post: post, slug: post.slug }
}"
v-html="post.title.rendered"
></NuxtLink>
Params truyền vào bao gồm data của bài viết ở dạng json và post slug.
Vì sao mình lại truyền 2 tham số này. Vì lúc get api ở bước trên, dữ liệu trả về bao gồm toàn bộ data các bài viết trong đó.
Nên ta chỉ cần truyền post object qua single và xử lý để hiển thị phía client. Nó sẽ không phải request api để lấy data của post nữa. Nhưng lúc render phía server chúng ta sẽ cần phải request api để lấy data, nên ở single chúng ta sẽ thêm hàm check xem post object truyền qua có null hay không.
Function asyncData của single post. Truy cập dữ liệu của route bằng cách sử dụng context
parameter, xem chi tiết Accessing dynamic route data
asyncData({ $axios, params, error }) { console.log('asyncData post single', params) if (!params.post) { const slug = params.slug return $axios .get(`https://techtalk.vn/wp-json/wp/v2/posts?_embed&slug=${slug}`) .then((res) => { if (!res.data.length) error({ statusCode: 404, message: 'Post not found!' }) return { post: res.data.length ? res.data[0] : [] } }) .catch(function(ex) { console.log('parsing failed', ex) error({ statusCode: 404, message: ex }) }) } else { return { post: params.post } } }
Còn truyền slug qua là bắt buộc, vì theo như cấu trúc project thì dynamic route tên là _slug.vue.
Trong bài viết sau, mình sẽ hướng dẫn cách deploy project Nuxt JS lên VPS, chờ đón nhé. Nếu có bất kỳ thắc mắc nào xin vui lòng để lại comment phía dưới.
Xem demo tại đây: https://techtalk.ntcde.com/blog
Project on github: https://github.com/trongcong/nuxtjs-minimal-blog
Bài viết rất hay. Cảm ơn bạn.