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
===