簡介
最近才學習了 React,現在要接觸服務端渲染,趁熱打鐵把 Next 學一下,關於 Next.js,可以移步到知乎討論:
知乎:關於 Next.js 的討論
初識Next.js
安裝
- Next.js 支援 Windows、Mac 和 Linux系統,均可安裝,但是前提是你已經安裝了 Node.js
- 建立示例專案的過程如下:
mkdir hello-next cd hello-next npm init -y npm install --save react react-dom next mkdir pages
使用
- 開啟
hello-next/package.json
,替換scripts
:"scripts": { "dev": "next", "build": "next build", "start": "next start" }
- 啟動
在瀏覽器中開啟 http://localhost:3000,你會看到頁面顯示 404 | This page could not be found.npm run dev
- 建立你的第一個頁面
- 建立
pages/index.js
,並輸入:const Index = () => ( <div> <p>Hello Next.js</p> </div> ); export default Index;
- 再次輸入
npm run dev
,就能看到效果了 - 上述案例中,我們在
pages/index.js
模組中預設(default)匯出了一個簡單的 React 元件
- 建立
- 試錯
- 嘗試著錯一次:將
pages/index.js
改為:const Index = () => ( <div> <p>Hello Next.js </div> ); export default Index;
- 重新啟動,瀏覽器顯示:
- 一般情況下,Next.js 將跟蹤此類錯誤並在瀏覽器中顯示,這便於我們快速發現錯誤,而你修改程式碼並儲存後,頁面將立即出現對應結果,而不會重新載入整個頁面,這是通過 webpack 的 模組熱替換 實現的,Next 預設支援這個功能
- 嘗試著錯一次:將
頁面間導航
Introduction
- 我們的應用程式雖然很簡單,只有一個頁面,但是我們可以新增任意多個頁面,例如:
- 建立
pages/about.js
來新建 “About” 頁面const About = () => ( <div> <p>About Page</p> </div> ); export default About;
- 修改
pages/index.js
:const Index = () => ( <div> <p>Hello Next.js</p> <a href="http://localhost:3000/about">This is a link to About-Page</a> </div> ); export default Index;
- 之後我們可以通過 http://localhost:3000/about 來訪問該頁面
- 之後,我們需要連線兩個頁面,首先想到的是可以用一個 HTML 的
<a />
標籤實現,但是結果就是:瀏覽器會向伺服器請求下一頁並重新整理當前頁面,也就是這樣做並不會執行客戶端導航
- 建立
- 為了支援瀏覽器端導航,我們需要使用 Next.js 提供的
Link
元件,這個元件是通過next/link
匯出的,接下來我們將使用它 - 我們需要準備一個簡單的 Next.js 應用課程,請在
hello-next
下輸入:git clone https://github.com/zeit/next-learn-demo.git
- 現在我們進入
hello-next/next-learn-demo/1-navigate-between-pages
啟動程式:cd next-learn-demo/1-navigate-between-pages npm install npm run dev
- 開啟 http://localhost:3000/ 訪問該程式
使用Link元件
注意,接下來的操作均在
hello-next/next-learn-demo/1-navigate-between-pages
下完成的
- 在
pages/index.js
中新增:import Link from 'next/link'; export default function Index() { return ( <div> <p>Hello next.js</p> <Link href="/about"> <a>About Page</a> </Link> </div> ) }
- 在這裡,我們將
next/link
匯入為Link
,並按照如下的方式使用:<Link href="/about"> <a>About Page</a> </Link>
- 訪問 3000 埠可檢視結果
- 在這裡,我們將
- 這次點選連結同樣會導航到 “About” 頁面,這是客戶端導航,操作在瀏覽器中進行,而不向瀏覽器傳送請求,你可以通過開啟瀏覽器的 網路請求檢查器(network request inspector) 來驗證這一點
- 後退按鈕:
當你點選連結,再點選後退時,依然會切換到歷史記錄的上一頁,也就是next/link
為你完成了所有location.history
的操作
新增連結道具
- 或許你需要在連線中新增屬性或道具,比如你需要向連結中新增
title
屬性,我們可以這樣新增它:
檢視元素,可以看到結果如下:<Link href="/about"> <a title="About-Pages">About Page</a> </Link>
- 切記不可新增到錯誤的地方去,若寫成如下:
則會在控制檯中報錯:<Link href="/about" title="About-Pages"> <a>About Page</a> </Link>
- 實際上,
Link
元件上的標題道具無效,是因為 Link 只是一個包裝器元件,只接收href
和一些類似的道具。如果需要向其新增道具,則需要將道具新增到其子項,這種情況下,Link
元件的子代是錨標記
使用共享元件
Introduction
- 我們可以通過匯出 React 元件並將該元件放在
pages
目錄中來建立頁面,每個頁面的 URL 都是基於檔名的,由於匯出的頁面是 JavaScript 模組,因此我們也可以將其他 JavaScript 元件匯入其中 - 我們將建立一個公共的 Header 元件並將其用於多個頁面,最後我們將研究實現 Layout 元件,並瞭解它如何幫我們定義多個頁面的外觀
執行
- 我們之前已經安裝過了
next-learn-demo
,這裡直接使用:- 進入
hello-next/next-learn-demo/2-using-shared-components
,之後我們的操作也會在此目錄下
- 進入
- 執行:
npm install npm run dev
建立標題元件
- 下面建立一個 Header 元件,建立
2-using-shared-components/components/Header.js
:import Link from 'next/link'; const linkStyle = { marginRight: 15, }; const Header = () => ( <div> <Link href="/"> <a style={marginRight}>Home</a> </Link> <Link href="/about"> <a style={marginRight}>About</a> </Link> </div> ); export default Header;
- 現在,匯入 Header 元件並在頁面中使用它:
- 將
index.js
修改為:import Header from '../components/Header'; export default function Index() { return ( <div> <Header /> <p>Hello Next.js</p> </div> ) }
- 將
about.js
修改為:import Header from '../components/Header'; export default function Index() { return ( <div> <Header /> <p>This is the About Page</p> </div> ) }
- 啟動之後可以檢視結果
- 將
- 試錯:現在將
components
目錄改名為comps
,報錯如下:- 我們不需要將我們的元件放在一個特殊的目錄裡,也就是說,該元件目錄名稱可以取為任何,實際上,唯一特殊的目錄是
/pages
和/public
,你甚至可以在/pages
裡面建立元件.
- 我們不需要將我們的元件放在一個特殊的目錄裡,也就是說,該元件目錄名稱可以取為任何,實際上,唯一特殊的目錄是
佈局元件
本節依然是在
2-using-shared-components/
下完成的
- 我們將建立 Layout 元件,以實現各頁面上的通用樣式,在
components/MyLayout.js
中輸入:import Header from './Header'; const layoutStyle = { margin: 20, padding: 20, border: '1px solid #DDD' }; const Layout = props => ( <div style={layoutStyle}> <Header /> {props.children} </div> ); export default Layout;
- 完成操作後,我們可以在頁面中使用以下佈局:
- 在
pages/index.js
中輸入:import Layout from '../components/MyLayout'; export default function Index() { return ( <Layout> <p>Hello Next.js</p> </Layout> ) }
- 在
pages/about.js
中輸入:import Layout from '../components/MyLayout'; export default function About() { return ( <Layout> <p>This is the about page</p> </Layout> ) }
- 啟動,檢視樣式
- 在
- 試錯:將
MyLayout.js
中的{props.children}
刪除,再啟動,觀察結果:- 頁面上只保留了
Header
的內容,其他的均消失了
- 頁面上只保留了
渲染子元件
- 前一個試錯中,我們刪除了
{props.children}
,則Layout
無法呈現我們放入Layout
元素內的內容,如下所示:
但這只是建立佈局元件的一種方法,以下是其他方法。export default function About() { return ( <Layout> <p>This is the about page</p> </Layout> ); }
- 方法一:佈局為高階元件
// components/MyLayout.js import Header from './Header'; const layoutStyle = { margin: 20, padding: 20, border: '1px solid #DDD' }; const withLayout = Page => { return () => ( <div style={layoutStyle}> <Header /> <Page /> </div> ); }; export default withLayout;
// pages/index.js import withLayout from '../components/MyLayout'; const Page = () => <p>Hello Next.js</p>; export default withLayout(Page);
// pages/about.js import withLayout from '../components/MyLayout'; const Page = () => <p>This is the about page</p>; export default withLayout(Page);
- 方法二:頁面內容作為道具
// components/MyLayout.js import Header from './Header'; const layoutStyle = { margin: 20, padding: 20, border: '1px solid #DDD' }; const Layout = props => ( <div style={layoutStyle}> <Header /> {props.content} </div> ); export default Layout;
// pages/index.js import Layout from '../components/MyLayout.js'; const indexPageContent = <p>Hello Next.js</p>; export default function Index() { return <Layout content={indexPageContent} />; }
// pages/about.js import Layout from '../components/MyLayout.js'; const aboutPageContent = <p>This is the about page</p>; export default function About() { return <Layout content={aboutPageContent} />; }
建立動態頁面
Introduction
- 之前通過使用元件,我們建立了包含了多個頁面的小案例,之前為了建立一個頁面,我們必須新建一個檔案作為模組匯出,但是在一個真正的應用程式中,我們還需動態地建立頁面以顯示動態內容,接下來我們會使用 查詢字串 來實現這一點
- 我們將建立一個簡單的部落格應用,它在主頁上展示一個所有文章的列表,展示如下:
- 主頁有文章列表
- 點選某標題的連結,會出現對應的文章
- 主頁有文章列表
安裝設定
- 我們仍然使用之前安裝過的
next-learn-demo
,進入next-learn-demo/3-create-dynamic-pages
,接下來的一切也將在這個目錄下完成 - 執行
npm install npm run dev
新增文章列表
- 首先,我們在文章主頁新增標題列表,如下:
import Link from 'next/link'; import Layout from '../components/MyLayout.js'; const PostLink = props => ( <li> <Link href={`/post?title=${props.title}`}> <a>{props.title}</a> </Link> </li> ); export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> <PostLink title="Hello Next.js" /> <PostLink title="Learn Next.js is awesome" /> <PostLink title="Deploy apps with Zeit" /> </ul> </Layout> ) }
通過查詢字串傳遞資料
- 我們將通過查詢字串作為引數(也稱查詢引數)並傳遞資料,如:
// pages/index.js const PostLink = props => ( <li> <Link href={`/post?title=${props.title}`}> <a>{props.title}</a> </Link> </li> );
- 此例中,查詢引數是
title
,我們使用PostLink
來執行的操作 - 你也可以檢查
Link
元件的href
屬性,以此類推,你可以使用查詢字串傳遞任何型別的資料
- 此例中,查詢引數是
建立Post
頁面
- 現在我們需要建立 post 頁面來顯示部落格文章,為此,我們需要從查詢字串中獲得標題,
- 建立
pages/post.js
檔案:import { useRouter } from 'next/router'; import Layout from '../components/MyLayout'; const Page = () => { const router = useRouter(); return ( <Layout> <h1>{router.query.title}</h1> <p>This is the blog post content.</p> </Layout> ); }; export default Page;
- 啟動專案,並點選三個標題連結
- 上面的運作過程如下:
- 首先從
next/router
匯入並使用useRouter
函式,該函式返回 Next.js 的是router
物件 - 使用路由器(router)中的
query
物件,該物件儲存了所有查詢引數 - 然後,使用
router.query.title
獲取標題
- 首先從
- useRouter 函式的介紹:
- useRouter 允許你訪問頁面中的 router 物件,它是一個 React Hook,能與功能元件協同合工作
- 之前的示例中,useRouter 函式被放到預新增的頁面元件中,而下面示例中,useRouter 函式在
Content
元件中,預新增的元件是Page
,但是功能不變import { useRouter } from 'next/router'; import Layout from '../components/MyLayout'; const Content = () => { const router = useRouter(); return ( <> <h1>{router.query.title}</h1> <p>This is the blog post content.</p> </> ); }; const Page = () => ( <Layout> <Content /> </Layout> ); export default Page;
使用動態路由清理URL
Introduction
請確保你正在使用是 Next.js 9 或更高版本
接下來的操作都會在next-learn-demo/4-clean-urls
中進行,請調至指定目錄
- 我們已經知道了如何使用查詢字串建立動態頁面,指向我們的某個部落格文章的連結如:
而此連結要表達的卻是:http:// localhost:3000 / post?title = Hello%20Next.js
http:// localhost:3000 / p / hello-nextjs
動態路由
啟動專案,在
4-clean-urls/
下輸入:npm install npm run dev
我們將使用 Next.js 的 動態路由 功能,它允許你處理
/pages
動態路由現在我們將建立新頁面,並命名為
pages/p/[id].js
,這也是我們建立的第一個動態路由,步驟如下:- 首先,在
/pages
內新增資料夾/p
- 然後,你需要在
/p
資料夾中建立[id].js
,並在這些 js 檔案中新增如下內容:import { useRouter } from 'next/router'; import Layout from '../../components/MyLayout'; export default function Post() { const router = useRouter(); return ( <Layout> <h1>{router.query.id}</h1> <p>This is the blog post content.</p> </Layout> ); };
- 首先,在
前一頁是特殊的,它不會處理
/about
等靜態路由,而是會處理p/
之後的路由,例如,此頁面將處理/p/hello-next.js
,而/p/post-1/another
頁面名稱中的帶有方括號(
[]
)使其成為動態路由,你不能使頁面名稱的一部分成為動態名稱,而只能使全名成為動態名稱,例如,支援/pages/p/[id].js
,但不支援/pages/p/post-[id].js
建立動態路線時,我們在方括號([])之間新增了 id,這是頁面接受到查詢引數的名稱,因此對於
/p/hello-nextjs
,該query
物件將具有{ id: 'hello-nextjs' }
,我們可以使用 useRouter() 進行訪問現在,我們新的動態路由新增多個連結,修改
pages/index.js
:// pages/index.js import Layout from '../components/MyLayout'; import Link from 'next/link'; const PostLink = props => ( <li> <Link href="/p/[id]" as={`/p/${props.id}`}> <a>{props.id}</a> </Link> </li> ) export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> <PostLink id="Hello-Next.js" /> <PostLink id="Learn-Next.js" /> <PostLink id="Deploy-Next.js" /> </ul> </Layout> ) }
- 著重看看以下內容:
const PostLink = props => ( <li> <Link href="/p/[id]" as={`/p/${props.id}`}> <a>{props.id}</a> </Link> </li> )
- 在
<Link>
元素中,href
代表的是該頁面在pages
資料夾中的路徑,而as
代表的是該頁面在瀏覽器中的 URL 路徑
- 在
- 現在,你可以重新啟動專案,注意觀察 URL 的變化!
- 著重看看以下內容:
動態路由可以很好地和瀏覽器歷史記錄配合使用,而我們要做的就是將
as
新增到連結元件中
為頁面獲取資料
Introduction
接下來的操作都會在
next-learn-demo/6-fetching-data
中進行,請調至指定目錄
- 現在我們已經能建立一個相對完整的 Next.js 應用,但還沒有解決的是:如何從遠端資料來源中獲取資料?,Next.js 提供了一個標準 API 來獲取頁面所需的資料,即
getInitialProps 非同步函式
getInitialProps
只能新增到頁面匯出的預設元件中,在其他元件中是不會起作用的,它可以從遠端資料來源為指定頁面獲取資料,並將這些資料通過 props 傳遞到我們的頁面,它會同時在客戶端和伺服器上工作,因為它在兩個環境中都會被呼叫- 我們將利用
getInitialProps
構建一個應用程式來顯示有關 Batman TV Shows 的資訊,利用的是公開的 TVmaze API - 在即將演示的示例中,我們的主頁上有一個文章列表,現在我們來展示
Batman TV shows
的節目列表,我們將從遠端伺服器上獲取這些節目列表,而不是硬編碼- 在這個示例中,我們使用的是 TVMaze API 來獲取 TV shows 節目列表,這是一個搜尋電視節目的 API
安裝設定
- 進入
next-learn-demo/6-fetching-data
,輸入:npm install npm run dev
- 在瀏覽器中開啟 http://localhost:3000/ 檢視專案
獲取 Batman Shows 的資料
- 首先,我們需要安裝 isomorphic-unfetch,這是我們用來獲取資料的工具庫,這是瀏覽器的 fetch API 的一個簡單實現,但在客戶端和伺服器環境中都可以使用
npm install --save isomorphic-unfetch
- 將
pages/index.js
替換為以下內容:// pages/index.js import Layout from '../components/MyLayout'; import Link from 'next/link'; import fetch from 'isomorphic-unfetch'; const Index = props => ( <Layout> <h1>Batman TV Shows</h1> <ul> { props.shows.map(show => ( <li key={show.id}> <Link href="/p/[id]" as={`/p/${show.id}`}> <a>{show.name}</a> </Link> </li> )) } </ul> </Layout> ); Index.getInitialProps = async function() { const res = await fetch('https://api.tvmaze.com/search/shows?q=batman'); const data = await res.json(); console.log(`Show data fetched. Count: ${data.length}`); return { shows: data.map(entry => entry.show) }; }; export default Index;
- 我們著重分析下面這部分:
Index.getInitialProps = async function() { const res = await fetch('https://api.tvmaze.com/search/shows?q=batman'); const data = await res.json(); console.log(`Show data fetched. Count: ${data.length}`); return { shows: data.map(entry => entry.show) }; };
- 這是一個靜態非同步函式,可以新增到程式的任何頁面中,使用此函式,我們就可以獲取資料並作為 props 傳遞給我們的頁面
- 以下便是我們的抓取結果,資料被抓取後,將會作為 props 的 ‘show’ 屬性傳遞我們的頁面中
- 注意,我們之前有一行用於列印資訊的程式碼:
console.log(`Show data fetched. Count: ${data.length}`);
- 那麼到底是在伺服器端輸出呢,還是在瀏覽器端的控制檯輸出呢,現在重新整理一下瀏覽器,會發現之後服務端的控制檯顯示
- 在這種情況下,訊息只會在服務端輸出,因為我們的頁面是在服務端繪製的,所以,我們在服務端已經有了資料,沒有必要在客戶端再次獲取這些資料
- 那麼到底是在伺服器端輸出呢,還是在瀏覽器端的控制檯輸出呢,現在重新整理一下瀏覽器,會發現之後服務端的控制檯顯示
實現 Post 頁面
- 現在讓我們把 TV show 的詳細資訊新增到 post 中:將
pages/p/[id].js
替換為以下內容:// pages/p/[id].js import Layout from '../../components/MyLayout'; import fetch from 'isomorphic-unfetch'; const Post = props => ( <Layout> <h1>{props.show.name}</h1> <p>{ props.show.summary.replace(/<[/]?[pb]>/g), '' }</p> </Layout> ); Post.getInitialProps = async function(context) { const { id } = context.query; const res = await fetch(`https://api.tvmaze.com/shows/${id}`); const show = await res.json(); console.log(`Fetched show: ${show.name}`); return { show }; }; export default Post;
- 注意該頁的
getInitialProps
:Post.getInitialProps = async function(context) { const { id } = context.query; const res = await fetch(`https://api.tvmaze.com/shows/${id}`); const show = await res.json(); console.log(`Fetched show: ${show.name}`); return { show }; };
- 該函式的第一個引數是 context 物件,此物件包含一個
query
物件,我們用context.query
來獲取資訊,即id
物件,並使其在 TVMaze API 中獲取電視節目資料
- 注意該頁的
- 在這個
getInitialProps
函式中,我們新增了一個console.log
來列印節目的標題,現在我們看看它將列印到哪裡- 開啟伺服器和客戶端的控制檯,然後啟動專案,訪問 3000 埠
- 單擊第一個 Batman show 的標題
- 結果是:會在客戶端的控制檯輸出
- 與之前不同的是,我們這次只能在客戶端看到訊息,這是因為我們通過客戶端導航到了 post 頁面
- 當我們單擊連結時,由於該連結是被 Next.js 的
<Link>
元件包裝過的元件,所以頁面轉換將在瀏覽器中進行,而不會想伺服器發起請求 - 但是,如果你直接訪問的是 post 頁面,而不是點選連結(例如,你直接訪問 http://localhost:3000/p/975 ),訊息會被列印在服務端,而不是客戶端
為元件新增樣式
Introduction
接下來的操作都會在
next-learn-demo/7-styling-components
中進行,請調至指定目錄
- 對於 React,我們可以使用許多不同的技術來設定樣式,這些技術可以分為兩大類:
- 傳統的基於 CSS 檔案的樣式設計(包括 SASS、PostCSS 等)
- CSS in JS
- 傳統的基於 CSS 檔案的樣式設計(尤其是 SSR)需要考慮一堆的實際問題,因此我們在為 Mext.js 設定樣式時避免使用這種方法,相反,我們會在 JS 中使用 CSS,你可以使用它來設定某單個元件的樣式,而不是匯入 CSS 檔案
- 為此,我們需要認識一個新的框架:styled-jsx,這是Next.js 預裝了一個 CSS in JS 框架,它允許你為元件編寫熟悉的 CSS 規則,這些 CSS 規則對元件以外的任何內容(當然也包括子元件)都沒有影響,也就是說,你的 CSS 規則是有作用域的
安裝設定
- 進入
next-learn-demo/7-styling-components
- 輸入
npm install npm run dev
- 啟動專案,訪問應用
設定主頁的樣式
現在我們在主頁中新增一些樣式,進入
pages/index.js
:// pages/index.js import Layout from '../components/MyLayout'; import Link from 'next/link'; function getPosts() { return [ { id: 'hello-nextjs', title: 'Hello Next.js' }, { id: 'learn-nextjs', title: 'Learn Next.js is awesome' }, { id: 'deploy-nextjs', title: 'Deploy apps with ZEIT' } ]; } export default function Blog() { return ( <Lauout> <h1>My Blog</h1> <ul> { getPosts().map(post => ( <li> <Link href="/p/[id]" as={`/p/${post.id}`}> <a>{post.title}</a> </Link> </li> )) } </ul> <style jsx>{` h1, a { font-family: 'Arial'; } ul { padding: 0 } li { list-style: none; margin: 5px, 0; } a { text-decoration: none; color: red; } a:hover { opacity: 0.6; } `}</style> </Lauout> ) }
- 那個
<style jsx>
元素,就是我們編寫 CSS 規則的地方 - 現在可以啟動專案,檢視結果
- 那個
上面的程式碼中,我們沒有直接在
<style>
標籤內編寫樣式程式碼,而是寫在模板字串({``}
)裡面的,Next.js 預設支援 babel 語法,而styled-jsx
可以看做 babel 的一個外掛,它將解析所有 CSS 並將其應用於構建工程
CSS樣式和巢狀元件
現在我們將會對主頁做一些更改,我們將會像這樣隔離 Link 元件:
import Layout from '../components/MyLayout'; import Link from 'next/link'; function getPosts() { return [ { id: 'hello-nextjs', title: 'Hello Next.js' }, { id: 'learn-nextjs', title: 'Learn Next.js is awesome' }, { id: 'deploy-nextjs', title: 'Deploy apps with ZEIT' } ]; } const PostLink = ({ post }) => ( <li> <Link href="/p/[id]" as={`/p/${post.id}`}> <a>{post.title}</a> </Link> </li> ); export default function Blog() { return ( <Layout> <h1>My Blog</h1> <ul> {getPosts().map(post => ( <PostLink key={post.id} post={post} /> ))} </ul> <style jsx>{` h1, a { font-family: 'Arial'; } ul { padding: 0; } li { list-style: none; margin: 5px 0; } a { text-decoration: none; color: blue; } a:hover { opacity: 0.6; } `}</style> </Layout> ); }
- 執行後發現:h1 的樣式還在,但是連結已經失效了,如下:
- 執行後發現:h1 的樣式還在,但是連結已經失效了,如下:
上述結果顯示:CSS 樣式規則對子元件中的元素沒有影響,
styled-jsx
這個特性可以幫助你管理大型的應用程式的樣式,在這種情況下,我們需要直接設定子元件的樣式,在我們的示例中,我們需要對 Link 元件執行以下操作:const PostLink = ({ post }) => ( <li> <Link href="/p/[id]" as={`/p/${post.id}`}> <a>{post.title}</a> </Link> <style jsx>{` li { list-style: none; margin: 5px 0; } a { text-decoration: none; color: blue; font-family: 'Arial'; } a:hover { opacity: 0.6; } `}</style> </li> );
- 擴充瞭解:全域性選擇器
全域性樣式
有時我們確實需要更改子元件內部的樣式,尤其是在使用支援 React 的 markdown 時
- 我們需要安裝
react-markdown
npm install --save react-markdown
- 我們需要安裝
下面就是全域性樣式派上用場的地方,現在就來試試利用
styled-jsx
新增一些全域性樣式,開啟pages/p/[id].js
:import { useRouter } from 'next/router'; import Markdown from 'react-markdown'; import Layout from '../../components/MyLayout'; export default () => { const router = useRouter(); return ( <Layout> <h1>{router.query.id}</h1> <div className="markdown"> <Markdown source={` This is our blog post. Yes. We can have a [link](/link). And we can have a title as well. ### This is a titlr. And here's the content. `} /> </div> <style jsx global>{` .markdown { font-family: 'Arial'; } .markdown a { text-decoration: none; color: red; } .markdown a:hover { opacity: 0.6; } .markdown h3 { margin: 0; padding: 0; text-transform: uppercase; } `}</style> </Layout> ); };
- 注意,markdown 語法部分的縮排不可修改!!!
- 我們定義的 style 作用於整個
<div>
標籤部分,也就是作用於全域性,雖然這樣很方便,但是還是建議寫帶有作用域的樣式 - 儘管如此,這依然是一個比普通樣式標籤更好的解決方案,使用 styled-jsx 時,所有必要的特定於瀏覽器廠商字首和 CSS 校驗都通過了一個 Babel 外掛完成了,這並不會導致額外的開銷
部署 Next.js 應用程式
Introduction
接下來的操作都會在
next-learn-demo/8-deploying
中進行,請調至指定目錄
- ZEIT Now 是將應用程式部署到生產環境的最簡單和可擴充套件的方法,但是,你可以部署 Next.js 應用程式,而且它相對來講比較簡單
- 現在我們將部署 Next.js 應用程式
- 進入
next-learn-demo/8-deploying
,輸入命令:npm install npm run dev
部署到 ZEIT Now
- 我們來看一下
package.json
檔案的script
配置段:"scripts": { "dev": "next", "build": "next build", "start": "next start" },
- 首先,我們需要為生產環境編譯我們的 Next.js 應用程式,它將生成一組優化的用於生產環境的程式碼:
npm run build
- 然後,你需要啟動 Next.js 應用程式並監聽某個埠,此伺服器將執行伺服器端渲染並返回靜態頁面(使用上述命令編譯)
npm run start
- 現在可以檢視效果:http://localhost:3000
執行兩個例項
- 現在我們將為我們的應用程式啟動兩個例項,通常這樣是為了橫向擴充套件我們的應用程式
- 首先,將
package.json
中的script
配置端替換為以下的內容:"scripts": { "dev": "next", "build": "next build", "start": "next start -p %PORT%" }
- 我們更改了
start
指令碼,它解除安裝接受一個代表埠號的引數來啟動應用程式 - 注意,
%PORT%
是對於 Windows 來說的,Linux 對應的是$PORT
- 我們更改了
- 現在構建我們的應用程式:
npm run build
- 現在我們需要安裝一個新的包,即:
cross-env
,我們在全域性環境下安裝:npm install cross-env -g
- 同時開啟兩個終端,分別在終端中輸入:
cross-env PORT=8000 npm start cross-env PORT=9000 npm start
- 在 Linux 上,則直接開啟兩個命令列終端,並分別在每個命令列終端上執行:
PORT=8000 npm start PORT=9000 npm start
- 在 Linux 上,則直接開啟兩個命令列終端,並分別在每個命令列終端上執行:
- 現在,在瀏覽器分別檢視:http://localhost:8000 和 http://localhost:9000
- 可以得到的結果是,你只需要對應用程式構建一次。然後就可以在任意多個埠上啟動它
結語
求進之路,持之謙卑,此教程是從官網理解翻譯來的,增刪了一些知識,我不在乎別人怎麼評價,我已經進步了…
本作品採用《CC 協議》,轉載必須註明作者和本文連結