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
只在服務器中渲染,事件處理如onClcik
won'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
===