Next.js文档自定义AppDocument,getInitialProps翻译

Posted wydumn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Next.js文档自定义AppDocument,getInitialProps翻译相关的知识,希望对你有一定的参考价值。

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, MapSet.

初始页面的加载只会在服务器运行。getInitialProps只会在客户端通过next/linknext/router导航到其他路由时执行。

Context对象

getInitialProps只接收一个参数contextcontext对象中有如下属性:

  • 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中接收到的对象,再加上一个:

  • renderPageFunction 一个回调函数,同步执行React渲染逻辑。为了支持服务器渲染wrappers(如Aphrodite的renderStatic),对这个函数进行decorate是很有用的。

注意

  • Document只在服务器中渲染,事件处理如onClcikwon‘t work
  • <Main />之外的React组件不会被浏览器初始化,不要在其中添加应用的逻辑。如果在页面中有共用的组件(如菜单或者工具栏),使用App组件代替。
  • 客户端转换时不会调用DocumentgetInitialProps方法,页面静态优化时也不会调用。

定制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

===

以上是关于Next.js文档自定义AppDocument,getInitialProps翻译的主要内容,如果未能解决你的问题,请参考以下文章

自定义箭头 Swiper Slider + Next.js + Sass

Next.js:文档未定义

如何使用 Next.js 检查自定义服务器中是不是存在页面

直接访问网址时如何解决“无法读取未定义的属性'文档'”? Next.js 和 apexcharts 库

自定义 Next.js - getRequestHandler 和渲染函数之间的区别

Next.js - 页面没有让 GetServerSideProps 进入页面组件道具(使用自定义 _app.js)