getInitialProps
getInitialProps在頁面中啟用服務端渲染並允許你進行初始數據注入,也就是發送在服務器已經注入了數據的頁面。這有利於SEO。
getInitialProps會禁用自動靜態優化
getInitialProps是一個可以作為靜態方法添加到任意頁面的異步方法,看下面例子:
import fetch from 'isomorphic-unfetch'
function Page({ stars }) {
return <div>Next stars: {stars}</div>
}
Page.getInitialProps = async ctx => {
const res = await getch('https://api.github.com/repos/zeit/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
export default Page
或者使用類組件:
import React from 'react'
import fetch from 'isomorphic-unfetch'
class Page extends React.Component {
static async getInitialProps(ctx) {
const res = await fetch('https://api.github.com/repos/zeit/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
render() {
return <div>Next stars: {this.props.stars}</div>
}
}
export default Page
getInitialProps用於異步獲取一些數據,然后注入props.
服務端渲染時,將getInitialProps獲取的數據序列化,和JSON.stringify做的一樣。確保getInitialProps返回的對象是一個純對象,沒有使用Date, Map或 Set.
初始頁面的加載只會在服務器運行。getInitialProps只會在客戶端通過next/link或next/router導航到其他路由時執行。
Context對象
getInitialProps只接收一個參數context,context對象中有如下屬性:
pathname-當前路由,是pages文件加下頁面的路徑query-URL的請求字符串部分,解析為對象asPath-瀏覽器中顯示的真實路徑(包括query)的字符串格式req-HTTP請求對象(只服務端)res-HTTP響應對象(只服務端)err-若在渲染過程中遇到錯誤,返回Error對象
注意
getInitialProps不能使用在子組件中,只能在每個頁面的default export中使用- 若在
getInitialProps中使用只在服務端用的模塊,確保正確的import,否則會拖慢你的app
===
自定義App
Next.js使用App組件來初始化頁面。可以重寫App來覆蓋Next.js自帶的App,控制頁面初始化。允許你做這些事情:
- 在頁面改變時保持布局
- 導航頁面時保持狀態
- 使用
componentDidCatch來自定義錯誤處理 - 將其他數據注入頁面
在pages文件夾下創建_app.js文件來覆蓋原有的App:
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
// 這個方法使得每一個頁面都由服務器渲染,禁用了自動靜態優化功能,所以只有應用中每個單獨頁面都有阻塞數據的請求時才可以注釋掉此方法。
//
// MyApp.getInitialProps = async (appContext) => {
// const appProps = await App.getInitialProps(appContext)
// return { ...appProps }
// }
export default MyApp
Component的prop是當前活動的page,所以當切換路由時,Component會切換為新的page。因此,發送給Component的props都會被page接收。
pageProps是一個包含預加載頁面props的對象,如果頁面不使用getInitialProps,它就是一個空對象。
在你的`App`中添加一個自定義`getInitialProps`方法會禁用掉自動靜態優化。
===
自定義Document
Next.js跳過了周圍文檔標記的定義,所以用自定義Document來擴展應用的<html>和<body>標簽。
自定義Document也可以包含getInitialProps方法來表示異步的服務器渲染數據請求。
在pages文件夾下創建_document.js來覆蓋默認的Document文件,自定義Document如下
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps}
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
要正確渲染頁面,<Html>,<Head />,<Main />和 <NextScript>是必須的。
ctx對象就相當於getInitialProps中接收到的對象,再加上一個:
renderPage:Function一個回調函數,同步執行React渲染邏輯。為了支持服務器渲染wrappers(如Aphrodite的renderStatic),對這個函數進行decorate是很有用的。
注意
Document只在服務器中渲染,事件處理如onClcikwon't work<Main />之外的React組件不會被瀏覽器初始化,不要在其中添加應用的邏輯。如果在頁面中有共用的組件(如菜單或者工具欄),使用App組件代替。- 客戶端轉換時不會調用
Document的getInitialProps方法,頁面靜態優化時也不會調用。
定制renderPage
注意,之所以要定制`renderPage`,是因為在css-in-js庫中,需要包裹應用才能正確使用服務端渲染。(簡單地說,就是規定)
可選對象作為參數來進一步定制
import Document from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage
// ctx.renderPage = function() {
// ctx.renderPage({
// enhanceApp: App => App,
// enhanceComponent: Component => Component,
// })
// }
// ctx.renderPage把自己包裹在一個匿名函數中,這tm什么操作???
// 這tm就是上面說的,規定,你不包一下,就不能服務端渲染
ctx.renderPage = () =>
originalRenderPage({
// 用於包裹整個react樹
enhanceApp: App => App,
// 用於以每頁為單位包裝
enhanceComponent: Component => Component,
})
// 執行父類的`getInitialProps`方法,現在它包含了定制的`renderPage`
const initialProps = await Document.getInitialProps(ctx)
return initialProps
}
}
export default MyDocument
===
