Tạo Blog đơn giản với Nuxt JS và WordPress API

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/

Tạo Blog đơn giản với Nuxt JS và WordPress API
Tạo Blog đơn giản với Nuxt JS và WordPress API

Điều kiện

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ặc
yarn 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:

Cấu trúc thư mục của project Nuxt JS
Cấu trúc thư mục của project Nuxt JS

Để 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-sasssass-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 HeaderFooter, 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

Custom layout Nuxt JS
Custom layout Nuxt JS

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.

techtalk blog

Ở 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 jsonpost 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.

Chi tiết single post
Chi tiết single post

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

Oh My Zsh: Nâng cao trải nghiệm terminal với giao diện đẹp và các plugin tăng hiệu suất! Git cherry-pick là gì? Cách sử dụng và ví dụ Git Rebase: Gộp nhiều commit thành một để tối ưu hóa lịch sử commit 11 tính năng JavaScript mới tuyệt vời trong ES13 (ES2022) CSS diệu kỳ: Các thuộc tính CSS mà bạn có thể chưa biết Auto deploy projects với GitHub Actions – sử dụng ssh-action WordPress Gutenberg Block Server Side Render Add, Upload image trong Gutenberg Block Development Tạo Block Controls – Block Toolbar và Settings Sidebar trong WordPress Gutenberg Làm quen với các components thường dùng khi tạo Gutenberg Block

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.