2023.02.20

BUILD BLOG ĐƠN GIẢN – NextJS Phần 3

NextJS phần 3

Ở các phần trước chúng ta đã tìm hiểu cơ bản về NextJS, để giới thiệu hết tất tần tật về Next JS chắc chúng ta cần phải thêm vài bài viết nữa, nên chúng ta hãy bắt tay vào project đơn giản ngay cho nóng nào.

Triển khai Blog đơn giản

Đầu tiên mình xin giới thiệu đến các bạn techtask mà mình thường dùng trong dự án Nextjs: Next.Js + Tailwind CSS + TypeScript + ESlint. Tùy vào dự án mỗi team thì các bạn chọn công nghệ phù hợp nhé.

Bây giờ mình sẽ bắt đầu setup project Next JS:

  • Tạo project Next JS bằng lệnh: yarn create next-app –typescript blog-nextjs

Sau khi tải xong project bạn dùng lệnh: cd  blog-nextjs để vào thư mục project vào run lệnh start project: yarn dev

Setup project Next JS
Setup project Next JS
  • Vậy là chúng ta đã chạy được project, tiếp theo chúng ta sẽ install Tailwind CSS vào project bằng 2 câu lệnh sau:

npm install -D tailwindcss postcss autoprefixer

npx tailwindcss init -p

  • Chúng ta sẽ config cho các template sử dụng tailwindCSS như sau nhé:
TailwindCSS
TailwindCSS

 

TailwindCSS 2
TailwindCSS 2

 

  • Bạn tắt app và chạy lại lệnh: yarn dev để build lại nhé. Sau đó thêm vào 1 class màu background của Tailwind CSS “bg-gray-200” để test xem đã effect chưa nhé.
Tailwind CSS “bg-gray-200”
Tailwind CSS “bg-gray-200”

 

  • Và kết quả là background đã đổi màu gray của Tailwind CSS thành công.
Tailwind CSS thành công
Tailwind CSS thành công

 

Mình sẽ giải thích các thư mục của project NextJS

Các thư mục của project NextJS
Các thư mục của project NextJS
  • Components: Thư mục chứa các component cần thiết cho project (tự tạo)
  • Public: Thư mục chứa các tài nguyên như hình ảnh, files data,..
  • Styles: Thư mục chứa các files css
  • Pages: Chứa các page của trang web, tên folder trong thư mục pages sẽ là route cho các page (VD: trong pages của mình có thư mục post vậy thì mình sẽ có link vào các bài post như sau: localhost:3001/post/1  “1” là số id của bất kỳ bài post nào.) Và trong thư mục pages cũng có sẵn thư mục api: đây là phần để viết API xử lý backend cho hệ thống.

Bây giờ chúng ta bắt đầu xây giao diện cho blog đơn giản nhé:

Public/data/posts.json

[
 {
   "id": 1,
   "title": "10 Extensions VS Code người mới học HTML&CSS cần phải biết",
   "content": "Điều chính yếu của việc học lập trình đó chính là kiến thức về các ngôn ngữ, các công nghệ mới có trong mảng lập trình đó.\n\nTuy nhiên sau những khoảng thời gian tìm hiểu về syntax, những khái niệm lập trình nặng đô, những bài thuật toán \"nổ não\" thì bạn cũng có thể cho phép bản thân mình giải lao một chút với những thứ nhẹ nhàng hơn nhé!\n\nĐiều này không chỉ giúp việc học trở nên dễ thở hơn mà nó còn giúp quá trình chinh phục kiến thức của bạn trở nên thú vị hơn rất nhiều!\n\nNhư việc tìm hiểu những extension trong VS Code chẳng hạn. Nó không giúp bạn kiếm tiền ngay, nó cũng không phải thứ quan trọng nhất bạn cần học nhưng nó sẽ giúp bạn làm việc nhanh hơn và có cảm hứng hơn khi code.",
   "image": "post1.png",
   "author": "tuongho",
   "created_at": "2023-01-03T17:00:00.000Z"
 },
 {
   "id": 2,
   "title": "Top 10 kênh youtube bổ ích về ReactJS và phát triển web",
   "content": "ReactJS là một trong những framework lập trình được các nhà phát triển quan tâm và yêu thích. Hiện nay, có rất nhiều kênh Youtube về ReactJS và các ngôn ngữ lập trình, ở đó bạn có thể học, trải nghiệm thực tế và phân tích chuyên sâu.\n\nĐiều đặc biệt là chúng hoàn toàn miễn phí, bạn có thể học bất cứ khi nào có thời gian. Trong bài viết này, 200Lab sẽ giới thiệu đến bạn top 10 kênh Youtube về ReactJS và phát triển web bổ ích mà bạn nên biết nếu bạn đang quan tâm đến framework ReactJS.",
   "image": "post2.png",
   "author": "tuongho",
   "created_at": "2022-12-30T17:00:00.000Z"
 },
 {
   "id": 3,
   "title": "Tại sao nên sử dụng ReactJS để phát triển website?",
   "content": "Khi nhìn vào một website, bạn thấy điều gì thu hút bạn nhất? Phông chữ của website, giao diện người dùng hay những thiết kế? Hầu hết người dùng thích một website có giao diện người dùng thân thiện và dễ sử dụng, đó là nơi React xuất hiện.\n\nReactJS đã nổi lên như một trong những thư viện lớn nhất được các nhà phát triển và doanh nghiệp sử dụng trong vài năm qua. Trong bài viết này, chúng ta sẽ cùng tìm hiểu về những lý do vì sao ReactJS được xem là ngôn ngữ lập trình mà các developer nên lựa chọn trong tương lai.",
   "image": "post3.png",
   "author": "tuongho",
   "created_at": "2022-12-31T17:00:00.000Z"
 },
 {
   "id": 4,
   "title": "ReactJS vs React Native - Gà cùng một mẹ liệu có giống nhau?",
   "content": "Bạn có đang rối giữa ReactJS vs React Native? Nên chọn cái nào thì tốt hơn? Có thể tái sử dụng code của ReactJS cho React Native hay không? Hãy khám phá câu trả lời thông qua bài viết so sánh ReactJS vs React Native này bạn nhé!\n\nTrước khi đi vào so sánh chi tiết ReactJS vs React Native, hãy cùng tìm hiểu sơ qua lịch sử của hai công nghệ mạnh mẽ này.\n\nReactJS và React Native có một lịch sử khá là thú vị",
   "image": "post4.png",
   "author": "tuongho",
   "created_at": "2023-01-01T17:00:00.000Z"
 },
 {
   "id": 5,
   "title": "Docker là gì? Khi nào nên dùng Docker?",
   "content": "Docker là một trong những requirement xuất hiện liên tục trong những JD tuyển dụng backend developer trong những năm gần đây. Rồi nào là DevOps, rồi SRE, CI/CD đều cần phải biết Docker. Qua bài viết này mình hy vọng sẽ giúp các bạn hiểu được một chút về Docker và lý do tại sao lại cần nó như thế.",
   "image": "post5.png",
   "author": "tuongho",
   "created_at": "2023-01-02T17:00:00.000Z"
 }
]


Layouts/Main: chỉ thay đổi nội dung bài post, còn lại layout sẽ giữ nguyên header, footer.

import type { ReactNode } from 'react';
import { Header } from '../components/Header';
import { Footer } from '../components/Footer';


type IMainProps = {
 children: ReactNode;
};


const Main = (props: IMainProps) => (
 <>
   <main className="bg-slate-900 min-h-screen">
     <div className="container lg:px-56 px-4">
       <Header blogName={'Blog NextJS'} />
       <>{props.children}</>
       <Footer />
     </div>
   </main>
 </>
);


export { Main };

Components/Header

Components/Header
Components/Header

 

Component/Footer

Component/Footer
Component/Footer

 

Và đây sẽ là trang index chính của chúng ta với danh sách các bài post: pages/index. Sử dụng kết hợp layout Main. ở Index bạn có thể cấu hình thẻ Head của website theo cách của mình để tăng hiệu quả SEO nhé.

import Head from 'next/head';
import { Post } from '../components/Post';
import { Main } from '../layouts/Main';


export default function Home() {
 return (
   <>
     <Head>
       <title>Blog NextJS</title>
       <meta name="description" content="Generated by create next app" />
       <meta name="viewport" content="width=device-width, initial-scale=1" />
       <link rel="icon" href="/favicon.ico" />
     </Head>
     <Main>
       <Post />
     </Main>
   </>
 );
}


Components/Post:

import Link from 'next/link';
import JSON_POSTS from '../../public/data/posts.json';
export type PostType = typeof JSON_POSTS[number];
import moment from 'moment';
export const Post = () => {
 return (
   <>
     <div className="text-white py-4">
       <h1 className="text-4xl">Latest</h1>
       <div className="text-gray-500 py-4 border-b border-gray-700">
         A blog created with Next.js and Tailwind.css
       </div>
     </div>
     {JSON_POSTS?.map((post) => {
       return (
         <div key={post.id} className="py-10 border-b border-gray-700">
           <div className="text-gray-500">
             {moment(post.created_at).fromNow()}
           </div>
           <div className="text-white text-2xl py-2">{post.title}</div>
           <div className="flex flex-wrap gap-4 text-emerald-600">
             <div>NEXT-JS</div>
             <div>TAILWIND</div>
             <div>FEATURE</div>
           </div>
           <div className="text-gray-500 py-4">{post.content}</div>
           <div className="text-emerald-600">
             <Link href={`post/${post.id}`}>Read more →</Link>
           </div>
         </div>
       );
     })}


     <div className="text-emerald-600 flex justify-end mt-4">All Posts →</div>
   </>
 );
};


Giao diện hoàn chỉnh trang index:

Giao diện hoàn chỉnh trang index
Giao diện hoàn chỉnh trang index

 

Các tính năng:

Tính năng Static Site Generation

Tính năng Static Site Generation
Tính năng Static Site Generation

 

  • Để tạo page chi tiết của bài post: chúng ta chỉ cần tạo files như sau: pages/post/[id].tsx (File-system Routing)
import { PostDetail, PostType } from '../../components/Post';
import type { GetStaticPaths, GetStaticProps } from 'next';
import type { ParsedUrlQuery } from 'querystring';
import JSON_POSTS from '../../public/data/posts.json';


type PostDetailPageType = {
 post: PostType;
};
type Params = ParsedUrlQuery & {
 id: string;
};
export const getStaticPaths: GetStaticPaths = async () => {
 const ids = JSON_POSTS.map((c) => c.id);
 const paths = ids.map((id) => {
   return {
     params: { id: id.toString() },
   };
 });
 return { paths, fallback: false };
};


export const getStaticProps: GetStaticProps = async (context) => {
 const { id } = context.params as Params;
 const post = JSON_POSTS.find((c) => c.id.toString() === id);


 return {
   props: { post },
 };
};
export default function DetailPost({ post }: PostDetailPageType) {
 return (
   <>
     <PostDetail post={post} />
   </>
 );
}


  • Ở đây, bằng cách kết hợp 2 hàm GetStaticPaths và GetStaticProps để chạy ở lúc build time, nextJS sẽ build cho chúng ta các page html tĩnh, đây là tính năng SSG nổi trội của NextJS (Hoặc Next JS sẽ mặc định dùng Automatic Static Generation khi không dùng getServerSideProps(SSR) để build ra các file html tĩnh). 
  • Dưới đây là các trang html tĩnh của các bài post đầy đủ data được build ra sẵn, cho nên khi nhận request client sẽ trả về ngay lập tức.
SSR
SSR

 

  • Đây là cách Next.JS truyền dữ liệu từ phía server về client, data sẽ được lưu vào __NEXT_DATA__ để truy cập khi cần thiết.
Truyền dữ liệu từ phía server
Truyền dữ liệu từ phía server

 

  • Giao diện chi tiết bài post:
Giao diện bài post
Giao diện bài post

Tính năng Server Side Rendering

Tính năng Server Side Rendering:
Tính năng Server Side Rendering:

 

  • Tạo thêm thư mục pages/article/[id].tsx để test tính năng này nhé.
  • Khi nào thì hàm  getServerSideProps run:
    • Khi bạn request trực tiếp vào trang này thì getServerSideProps sẽ run ở lúc request time và trang của bạn sẽ được pre-render với kết quả trả về là props.
    • Khi bạn request trang này ở client side thông quan next/link, next/router NextJS sẽ gửi request API đến server và server sẽ chạy getServerSideProps.
  • Hàm getServerSideProps này chỉ sử dụng ở server side và chỉ được dùng trong thư mục pages, ở lúc runtime khi server nhận request sẽ thực hiện lấy data và build thành file html trả về client.
  • Bạn có thể dùng tính năng cache-control của NextJS để tăng thêm hiệu suất
import { PostDetail, PostType } from '../../components/Post';


import JSON_POSTS from '../../public/data/posts.json';


type DetailArticlePageType = {
 post: PostType;
};


export async function getServerSideProps({ params }: any) {
 const dataPost = JSON_POSTS.find((item) => item.id == params.id) || {};
 return {
   props: {
     post: dataPost,
   },
 };
}
export default function DetailArticle({ post }: DetailArticlePageType) {
 return (
   <>
     <PostDetail post={post} />
   </>
 );
}


Blog Next.JS
Blog Next.JS

 

Tính năng Client Side Rendering

Tính năng Client Side Rendering:
Tính năng Client Side Rendering:

 

  • Tính năng này rất hữu ích khi trang không cần index SEO, không cần pre-render hoặc nội dung update thường xuyên, bạn có thể sử dụng nó ở level component. 
  • Data sẽ được fetch ở lúc runtime (at time component mount) và render lại khi data change. 
  • Nhưng phải lưu ý, sử dụng CSR có thể làm ảnh hưởng performance của hệ thống, thời gian load trang.
  • Bên cạnh đó data fetch sẽ không được cache lại. Bạn có thể sử dụng SWR của nextJS để handle được phần này.
  • Tạo thêm thư mục pages/item/[id].tsx để test tính năng này nhé.
import { PostDetail, PostType } from '../../components/Post';
import { useRouter } from 'next/router';
import JSON_POSTS from '../../public/data/posts.json';
import { useEffect, useState } from 'react';


export default function DetailArticle() {
 const router = useRouter();
 const { id } = router.query;
 const [post, setPost] = useState<PostType>();
 const [isLoading, setLoading] = useState(false);


 useEffect(() => {
   i if (isLoading) return <p>Loading...</p>;


 return <>{post !== undefined ? <PostDetail post={post} /> : ''}</>;
}f (!router.isReady) return;
   setLoading(true);
   const dataPost = JSON_POSTS.find(
     (item) => item.id.toString() === id?.toString()
   );
   setPost(dataPost);
   setLoading(false);
 }, [router.isReady]);



Blog Next.JS
Blog Next.JS

 

Tổng kết

Như vậy, NextJS ngoài việc cung cấp cho chúng ta rất nhiều tính năng hữu ích thì nó còn là một framework triển khai nhanh chóng, mang hiệu suất cao.  Hy vọng qua bài viết giúp các bạn hiểu cơ bản về các tính năng của NextJS.

Link github project: https://github.com/hochituong96/blog-nextjs

Các bạn đọc thêm tài liệu ở trang chủ NextJS nhé:  https://nextjs.org/docs/getting-started

 

Các bài viết về NextJS :

Phần 1 : Giới thiệu về NextJS

Phần 2: Tính năng nổi bật NextJS

Phần 3: Build Blog đơn giản

0 Comments
Inline Feedbacks
View all comments