Phương pháp custom route cho bài viết trong Nuxtjs
Bài viết này sẽ hướng dẫn cách cấu hình đường dẫn linh động với Nuxtjs, hay còn gọi là custom route để tạo mã định danh cho bài viết và đưa vào đường dẫn, áp dụng cho cả hai cơ chế Server-side Rendering và Static Site Generator.
This post is also available in English
Các đường dẫn giả mạo xuất hiện ngày càng nhiều, vì vậy người dùng thường có thói quen kiểm tra trước khi quyết định có nên truy cập trang web hay không. Vì thế, các trang tin hay blog sẽ nhúng tựa đề bài viết vào đường dẫn, giúp người dùng nắm được khái quát nội dung, tăng tính thống nhất và độ tin tưởng của bài viết. Tuy nhiên, khuyết điểm của cách làm này đó là khi cần cập nhật tiêu đề của bài viết thì đường dẫn tới bài viết đó cũng sẽ thay đổi, đường dẫn cũ sẽ không thể truy cập được, gây khó chịu cho người dùng. Ngoài ra, chúng ta còn phải cập nhật tất cả các bài viết có liên kết tới bài viết đã đổi tên.
Việc cấu hình đường dẫn linh động cho một bài viết hay trang tin đã trở thành điều kiện hầu như phải có bởi độ linh hoạt và thân thiện với người dùng. Để giải quyết bài toán này, ta có thể áp dụng phương pháp tạo ra một mã định danh cho bài viết và đưa nó vào trong đường dẫn. Mình sẽ gọi phương pháp này là custom route.
Với việc dùng mã định danh, khi tựa đề bị thay đổi thì trang web vẫn có thể hiển thị đúng bài viết cần tìm dù người dùng truy cập vào bài viết từ đường dẫn cũ. Chúng ta sẽ cùng tìm hiểu cách để áp dụng custom route trong cả hai cơ chế Server-side Rendering và Static Site Generator với Nuxtjs.
Khởi tạo project
Nếu các bạn đã có project và chỉ muốn tìm hiểu cách để áp dụng custom route thì có thể bỏ qua phần này.
Bài viết chỉ tập trung vào việc thiết lập đường dẫn linh động cho bài viết nên sẽ không hướng dẫn cụ thể cách tạo một project Nuxtjs. Nếu mới bắt đầu, các bạn có thể bắt đầu tại đây.
Sau khi đã khởi tạo project, ta tiến hành tạo ra một số bài viết lưu trong thư mục content/blog
. File được lưu dưới định dạng markdown, với tên file là mã định danh duy nhất của bài viết. Trong thực tế, các bài viết sẽ được tạo/chỉnh sửa bằng các CMS tool như Netlify, Hugo hay Wordpress.
Dưới đây là định dạng đơn giản của file markdown:
---
title: Apply custom route for blog in Nuxtjs
subtitle: Subtitle
---
Content
Ta sẽ dùng thư viện Nuxt Content để đọc file bài viết dưới định dạng markdown, bắt đầu bằng việc import thư viện vào trong project:
yarn add @nuxt/content
Sau đó, thêm thư viện @nuxt/content
vào trong cấu hình modules
trong file nuxt.config.js
:
{
…
modules: \[
'@nuxt/content'
],
…
}
Tạo trang cho bài viết
Tạo file bài viết ở pages/_slug/index.vue,
trong đó, slug
là đường dẫn liên kết với bài viết:
Tại trang bài viết, ta tiến hành lấy mã định danh bài viết và tải về nội dung trong hàm asyncData()
:
async asyncData({ $content, params }) {
// Lấy post ID từ slug
const postID = params.slug.split('-').pop()
// Tải về nội dung của bài viết
const post = await $content('blog', postID).fetch().catch(_ => {})
return {
post
}
}
Tiếp theo, ta tiến hành kiểm tra xem mã định danh có hợp lệ hay không và cập nhật lại đường dẫn nếu cần:
async mounted() {
// Kiểm tra xem bài viết có tồn tại hay không
if (!this.post) {
window.location.replace('/404') // Đưa về trang 404
} else {
// Khởi tạo đường dẫn bài viết theo tiêu đề
const postID = this.$route.params.slug.split('-').pop()
const urlTitle = this.post.title
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '')
const slug = `${urlTitle}-${postID}`
// Kiểm tra xem đường dẫn có khớp với đường dẫn hiện tại hay không
// Nếu không, ta tiến hành cập nhật lại đường dẫn
if (slug !== this.$route.params.slug) {
// Sử dụng pushState để tránh page reload
window.history.pushState('', '', slug)
}
}
},
Thêm vài bước để render trang web:
<template>
<div>
<h1>{{post ? post.title : ''}}</h1>
<h2>{{post ? post.subtitle : ''}}</h2>
<nuxt-content :document="post" />
</div>
</template>
Vậy là ta đã tạo ra một trang cho bài viết với đường dẫn linh động theo tiêu đề. Các bạn có thể tiến hành chạy thử bằng lệnh yarn dev
hoặc tổ hợp yarn build
và yarn start
. Nếu ta bỏ đi vài ký tự tiêu đề trong đường dẫn thì trang web sẽ tự sửa lại đường dẫn đúng, còn nếu ta thay đổi mã định danh thì sẽ được chuyển sang trang 404.
Chúng ta có thể thấy tính năng này đã chạy ổn trong cơ chế Server-side Rendering.
Áp dụng trong cơ chế Static Site Generator
Nếu đang sử dụng cơ chế Static Site Generator để build trang web, điều đầu tiên cần làm là tạo ra đường dẫn cho các bài viết để Nuxtjs khởi tạo trang bài viết trong quá trình xây dựng bản web tĩnh.
Trong file nuxtjs.config.js
:
…
target: 'static', // Kích hoạt full static mode
generate: {
async routes() {
const { $content } = require('@nuxt/content')
// Lấy ra tất cả bài viết
const files = await $content('blog').only(['slug', 'title']).fetch()
// Trả về danh sách đường dẫn để Nuxtjs khởi tạo trang bài viết
return files.map(
file => `/${file.title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '')}-${file.slug}`
)
},
crawler: false
},
…
Sau khi chạy lệnh yarn generate
, website sẽ được build và lưu trữ trong thư mục dist
.
Trong quá trình host web sẽ xuất hiện tình trạng trang web được đưa về trang 404 khi người dùng nhập sai vài ký tự trong thanh địa chỉ, mặc dù mã định danh của bài viết vẫn đúng. Đây không phải là lỗi của Nuxtjs mà là do các tool host static web chỉ map các đường dẫn tới các đường dẫn thư mục trong dist
, và đưa về trang 404 nếu như không tìm thấy.
Việc khắc phục tình trạng này tùy thuộc vào tool host web mà chúng ta đang sử dụng. Sau đây là một trong những cách xử lý:
- Khởi tạo một trang đệm với content trống.
- Cấu hình tool host web để trỏ vào trang này khi không tìm thấy bài viết.
- Ở trang đệm này, kiểm tra xem bài viết có hợp lệ hay không. Nếu có, đưa về trang bài viết, nếu không thì đưa về trang 404.
Sau đây là một ví dụ khi host trang web bằng http-server, giả sử ta tạo ra một trang đệm là pages/blank.vue
.
Nội dung trang pages/blank.vue
:
<template>
<div></div>
</template>
<script>
export default {
name: 'BlankPost',
async mounted() {
// Lấy post ID từ url , Ex: "http://localhost:8080/blank/?%2F-fake-202206280909" => "202206280909"
const postID = this.$route.fullPath.split('?').pop().replaceAll('%2F', '').split('-').pop()
// Tải về nội dung của bài viết
const post = await this.$content('blog', postID).fetch().catch(_ => {})
// Kiểm tra xem bài viết có tồn tại hay không
if (post) {
// Đưa về trang bài viết
const urlTitle = post.title
.toLowerCase()
.replace(/ /g, '-')
.replace(/[^\w-]+/g, '')
const slug = `${urlTitle}-${postID}`
window.location.replace(`/${slug}`)
} else {
// Đưa về trang 404
window.location.replace('/404')
}
}
}
</script>
Chạy trang web và xem kết quả qua các bước: yarn generate
, cd dist
và http-server -P http://localhost:8080/blank?
.
Kết
Trên đây là hướng dẫn cấu hình đường dẫn linh động cho một bài viết hay trang tin bằng Nuxtjs. Với phương pháp custom route này, người dùng vẫn có thể truy cập bài viết bằng đường dẫn cũ dù chúng ta đã thay đổi một tựa đề mới. Cơ chế này càng có ý nghĩa hơn đối với những bài viết mang tính tường thuật trực tiếp (live) khi cần phải thay đổi tựa đề nhiều lần trong một bài viết.
Phần quản lý nội dung của bài post không được đề cập trong bài viết này, nếu bạn muốn xây dựng một hệ thống hoàn chỉnh, hãy bắt đầu bằng cách tìm đến các CMS tool điển hình như Netlify CMS, Flextype hay Hugo.
Hy vọng bài viết này sẽ giúp ích cho các dự án tương tự sắp tới của bạn.