動手練一練,使用 React 和 Next.js 做一個簡單的博客網站(下)

作者:Craig Bucklere
原文:Build a Blog with React and Next.js(sitepoint)
字數:2500 字 (非直譯)
閱讀: 5分鐘
大家好,在《動手練一練,使用 React 和 Next.js 做一個簡單的博客網站(上)》和 《動手練一練,使用 React 和 Next.js 做一個簡單的博客網站(中)》這兩篇文章里,我們一起完成了一個基于 MakeDown 文檔為內容頁源的博客網站,學習了動態(tài)路由(Dynamic Routes)、Static Generation 相關的知識及應用,本篇文章,我們將學習如何使用服務端渲染(Server-side Rendering)、客戶端渲染(Client-side Rendering)的內容,最后再和大家聊聊如何編譯項目、部署站點。
一、使用服務端渲染(Server-side Rendering)
服務端渲染(SSR)發(fā)生在每次頁面請求時,其相關內容邏輯比如 NodeJs 的數據請求都交由服務端處理完成后,再將內容呈現給訪問的用戶,因此通過此技術可以做一些復雜的業(yè)務邏輯,比如每個用戶登錄成功后,呈現不同的文章內容。
在《動手練一練,使用 React 和 Next.js 做一個簡單的博客網站(中)》這篇文章里,我們使用了getStaticProps() 這個方法在項目編譯時(build)處理生成 MD 動態(tài)路由相關的邏輯。如果要使用服務端渲染,我們可以使用 getServerSideProps() 函數,在頁面請求時由服務端執(zhí)行此函數邏輯,完成數據的渲染。
這里我們要實現一個簡單的功能,在 about.js 這個頁面,去請求一個笑話網站的API服務(https://official-joke-api.appspot.com/jokes/random),我們通過服務端的形式去請求數據,最后將數據處理完成后,再呈現給用戶。廢話不多說,我們開始動手實現吧。
1、我們現在 /pages/about.js 頁面里添加 getServerSideProps() 函數,這個方法里我們使用 node-fetch 模塊,獲取 API 數據后,通過 props 將返回的數據通過組件屬性進行傳遞(請看第2點的相關代碼),示例代碼如下:
// fetch a random joke (generated on every reqest)
export async function getServerSideProps() {
const res = await fetch('https://official-joke-api.appspot.com/jokes/random');
return {
props: {
data: await res.json()
}
}
}
2、接下來我們來編寫組件 JSX 的部分,接收 getServerSideProps() 返回的數據,示例代碼如下
export default function About({ data }) {
return (
<Layout hero="keyboard.jpg">
<Head>
<title>About us</title>
<meta name="description" content="What we do." />
</Head>
<h1>About us</h1>
<p>Some information about us.</p>
<h2>A random { data.type } joke</h2>
<p className="setup">{ data.setup }...</p>
<p className="punchline">{ data.punchline }</p>
<style jsx>{`
.setup { font-weight: bold; }
.punchline { font-style: italic; }
`}</style>
</Layout>
);
}
你可能注意到了,上述代碼使用了 styled JSX 語法添加 CSS 樣式。
到這里,服務端渲染的功能就完成了,我們通過 npm run dev 重啟下 next.js 服務器,在瀏覽器上點擊 http://localhost:3000/about 預覽,就會看到如下圖所示的界面:

二、客戶端渲染(Client-side Rendering),實現暗黑瀏覽模式
Next.js 會在編譯的時機(build time)或頁面請求時在服務端完成 React 組件的渲染邏輯,但是有些功能完全可以交給客戶端端瀏覽器處理,也是官方說道的客戶端激活 (client-side hydration 不知道怎么翻譯,暫且這么叫吧,借用vue相關文檔的翻譯)比如我們要實現暗黑和白天模式的切換,這里我們就可以用到 React.useEffect() 這個鉤子處理函數。
大家有點需要格外注意,在服務端,有個細節(jié)需要注意,在服務端沒有 window 這個頂層對象,如下段代碼所示,執(zhí)行時將會有錯誤提示:
// THIS WILL FAIL!
const [href, setHref] = React.useState(window.location.href);
為了修復這個問題,我們可以將初始值賦值null。
1、首先我們新建個 components/themetoggle.js 暗黑和白天瀏覽模式的切換組件,這里使用 useEffect() 這個鉤子函數實現其邏輯,示例代碼如下:
import React from 'react';
import styles from './themetoggle.module.scss';
export default function Themetoggle() {
const
toggleStore = 'themeToggle',
[theme, setTheme] = React.useState(null);
React.useEffect(() => {
if (!theme) {
setTheme(
localStorage.getItem(toggleStore) ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
);
}
if (theme) {
const body = document.body;
body.classList.remove('dark', 'light');
body.classList.add(theme);
localStorage.setItem(toggleStore, theme);
}
}, [theme])
return (
<button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className={ styles.themetoggle + ' ' + styles[theme] }
title="switch theme"
/>
);
}
由于服務端無法訪問客戶端模式的初始值,我們先初始化為null,然后當組件成功加載至客戶端時,我們使用 useEffect() 這個鉤子函數進行黑暗和白天模式的邏輯處理。你可能注意到為此組件,我單獨引用了一個 CSS 文件,具體源碼,你可以在文末點擊閱讀原文鏈接,在本項目的完整源碼中找到對應的CSS鏈接。
2、最后,我們將此組件添加至 components/header.js 的 <Header> 組件中,示例代碼如下:
import Navmenu from './navmenu';
import Themetoggle from './themetoggle';
import Link from 'next/link';
export default function Header({ hero }) {
hero = '/images/' + (hero || 'orb.jpg');
return (
<header>
<p className="logo">...</p>
<Themetoggle />
<Navmenu />
<figure>...</figure>
</header>
);
}
接下來,重新運行 Next.js 服務,每個頁面都添加了黑暗與白天切換的模式功能 ,效果如下 GIF 動圖所示:

三、編譯發(fā)布網站
到這里為止,一個完整的簡單博客網站到這里就介紹完了,如果你想在生產環(huán)境部署網站的話,先停在站點 Ctrl | Cmd + C , 然后運行如下命令:
npm run build
編譯完成后,你會發(fā)現項目的根目錄里多了個 .next 文件夾,你可以將此文件夾所有的文件內容拷貝到 Node.js 10 以上的服務器上進行部署。
你可以在此文件夾里運行 npm run start,在測試環(huán)境下去測試生產環(huán)境的站點。
四、生成靜態(tài)HTML頁面
Next.js 允許你將現有的站點生成靜態(tài)的 HTML 頁面(除了需要服務端渲染的界面),如果你為頁面定義了 getServerSideProps() 服務端渲染相關的函數,導出將會失敗。
如下所示,需要按照命令的先后進行運行,將站點生成靜態(tài)頁面:
npx next build
npx next export
編譯完成后,你將會在項目的根目錄看到 out 這個文件夾,如下圖所示:

五、接下來你可以動手試一試
本文給大家展示了 Next.js 基礎內容,并實現了博客的基礎功能,接下來你可以繼續(xù)完善博客的功能,不妨親自動手試一試為博客網站添加以下功能 :
添加404頁面配置 實現博客文章列表頁的分頁功能 添加文章標簽功能 添加 SSR 功能 嘗試從其他內容源獲取博客文章內容(服務端API數據接口、wordpress等)
六、相關閱讀
《動手練一練,使用 React 和 Next.js 做一個簡單的博客網站(上)》
《動手練一練,使用 React 和 Next.js 做一個簡單的博客網站(中)》
結束語
到這里,本案例就介紹完了,本案例的完整源碼,你可以點擊閱讀原文下載本案例的完整源碼。
Next.js 是一個靈活的應用程序框架,可以幫助你構建任何類型的 web 項目,對于博客網站這類需求,很容易滿足實現。Next.js 這個框架已經很成熟,而且定期維護,如果你已很熟悉 React 這個前端框架,選擇 Next.js 構建站點將會是一個很不錯的選擇。
