Nuxt.js Fundamental ตอนที่ 5 - Nuxt Content และ Async Data

Published on

เขียนวันที่ : Aug 22, 2020

LastModified on

(อัพเดท : Mar 19, 2022)

Discord

Nuxt Content คืออะไร?

Nuxt Content เป็น Module ที่ทำให้เราสามารถที่จะอ่าน JSON, YAML, Markdown หรือ CSV ใน folder content เหมือนกับการใช้พวก MongoDB API หรือเป็น QueryBuilder API นั่นเอง

ข้อดีของ Nuxt Content คือ

  • เร็วและรองรับ hot reload ในการ development
  • รองรับ Full-text search
  • เขียน Vue Components ใน Markdown ได้
  • QueryBuilder API อารมณ์คล้ายๆ MongoDB API
  • รองรับหลาย format ดังที่กล่าวไป CSV, YAML, JSON, XML, Markdown

วิธีการใช้งาน Nuxt Content

ก็เพียงแค่ install ผ่าน NPM หรือ yarn ได้เลยครับ

npm install @nuxt/content

จากนั้นที่ไฟล์ nuxt.config.js ก็ไปเพิ่ม @nuxt/content ไปในส่วนของ modules

  modules: [
    '@nuxt/content'
  ]
}

วิธีการทำงานของ Nuxt Content คือ

  1. อ่านค่าจาก raw data (แล้วแต่ format เป็นอะไร)
  2. ตัว Nuxt จะ transform raw data เป็นรูปแบบ JSON tree
  3. ใช้ $content ในการ fetch data เพื่อมาแสดง (รองรับ async/await)
  4. แสดงผลด้วยการใช้ <nuxt-content> Component (เฉพาะ Markdown)

เริ่มต้นเขียน Content

ลองด้วย Markdown ก่อนครับ สร้างไฟล์ hello.md ใน folder /content/posts ครับ

---
title: Hello World
description: Learn how to use @nuxt/content.
---

# Hello

This is <nuxt-link to="/">Home</nuxt-link>

This is paragraph

ซึ่ง Markdown ก็รองรับ Front matter ในรูปแบบ YAML (ใช้ - 3ตัว) ทำให้เรากำหนด key value เช่น title, description หรือจะเป็น published_date, author อะไรก็ได้

ทีนี้ ตัว Nuxt Content มันก็จะ generate ข้อมูลเป็น JSON tree หน้าตาประมาณนี้

{
  body: Object
  title: "Hello World"
  description: "Learn how to use @nuxt/content."
  dir: "/"
  extension: ".md"
  path: "/hello"
  slug: "hello"
  toc: Array
  createdAt: DateTime
  updatedAt: DateTime
}

ตัว createdAt และ updatedAt จะใช้เวลาตอนสร้างไฟล์ แต่เราสามารถ override ได้ โดยการกำหนดค่าใน Front Matter

ดูข้อมูลเพิ่มเติม Nuxt Content - Writing Content

หรือ เราสามารถใช้ไฟล์ YAML หรือ jSON ก็ได้ เช่น ไฟล์ /content/site.yml

site: https://devahoy.com
title: Devahoy

หรือไฟล์ /content/data.csv

id, name, age
1, John Doe, 25
2, Jane Doe, 23

Output ที่ได้ ก็คล้ายๆ กับ Markdown เพียงแค่เปลี่ยน body, extension และก็ path, slug ตามชื่อไฟล์นั่นเอง

Output ของ YAML

{
  'dir': '/',
  'slug': 'site',
  'path': '/site',
  'extension': '.yml',
  'site': 'https://devahoy.com',
  'title': 'Devahoy'
}

Output ของ CSV

  "dir": "/",
  "slug": "data",
  "path": "/data",
  "extension": ".csv",
  "body": [
    {
      "id": 1,
      "name": "John Doe",
      "age": 25
    },
    {
      "id": 2,
      "name": "Jane Doe",
      "age": 23
    }
  ]
}

การ Fetch ข้อมูล

ต่อมา เราจะ fetch data content กันนะครับ โดย Nuxt Content เราสามารถเรียกแบบ global ได้เลย ด้วย this.$content และ พวก plugins, asyncData, fetch, middleware เราสามารถเรียกผ่าน context ได้เลย เช่น context.$content

syntax ในการเรียกคือ

$content(path, options?)
  • path - ถ้าเป็น file จะได้ result เป็น Object ถ้าเป็น folder จะเป็น Array

เช่น ผมจะดึงข้อมูลในไฟล์ /content/posts/hello.md ผมก็เรียกแบบนี้

const post = await this.$content('posts/hello').fetch()

นอกจากนี้เรายังใช้พวก where, limit, sortBy, only ได้เช่น

const posts = await this.$content('posts')
  .only(['title', 'createdAt'])
  .sortBy('createdAt', 'asc')
  .limit(5)
  .skip(10)
  .where({
    isArchived: false
  })
  .search('welcome')
  .fetch();

รายละเอียดเพิ่มเติม Nuxt Content - Fetching Content

ตัวอย่าง เช่น ผมจะดึงข้อมูล /content/site.yml มาแสดงใน pages/site.vue โดยผมใช้ $content ใน function asyncData นั่นเอง

site.vue
<template>
  <h1>Ahoy!</h1>
  <p>Site : {{ site.site }}</p>
  <p>Title: {{ site.title }}</p>
</template>

<script>
export default {
  async asyncData({ $content }) {
    const site = await $content('site').fetch();

    return {
      site
    };
  }
};
</script>

การแสดงผล Content สำหรับ Markdown

สำหรับ Markdown มันค่อนข้างแตกต่างจากไฟล์อื่นนิดหน่อย เพราะมันต้อง Transform markdown เป็น HTML body แล้วเก็บอยู่ใน object ของ JSON ฉะนั้น เวลาแสดงผลใน Vue Component ก็เลยต้องใช้ <nuxt-content นั่นเอง ตัวอย่างเช่น

ไฟล์ /pages/hello.vue

pages/hello.vue
  <article>
    <h1>{{ post.title }}</h1>
    <nuxt-content :document="post" />
  </article>
</template>

<script>
export default {
  async asyncData ({ $content }) {
    const post = await $content('posts/hello').fetch()

    return {
      post
    }
  }
}
</script>

เรื่อง CSS นิดหน่อย <nuxt-content> จะทำการเพิ่มคลาส .nuxt-content ให้เราอัตโนมัติ ทีนี้ ถ้าใครอยาก override หรือปรับ css ก็ต้องใช้ selecotr แบบนี้ครับ

.nuxt-content h1 {
  /* my custom h1 style */
}

Hints & Questions?

  • เอ๊ะ แล้วถ้าเราอยากทำเป็นคล้ายๆ Blog โดยสร้างจากไฟล์ Markdown สมมติ มี 10 ไฟล์ และต้องมาสร้างเพจ pages/**.vue อีก 10 ไฟล์ด้วยรึเปล่านะ? เราจะทำ dynamic route ยังไงดี? ลองคิด และลองประยุกต์ใช้กันดูนะครับ :)
  • ลองเล่น QueryBuilder API เช่น เราอยาก query โดยกำหนดเงื่อนไข .where() หรือกำหนด sortBy ยังไงดี?
Buy Me A Coffee
Discord