如何从 Next.js 正确键入 _document.tsx 文件?

cbd*_*per 4 typescript next.js

我从关于如何使用的官方文档示例中获得了大部分代码styled-components

https://github.com/vercel/next.js/blob/canary/examples/with-styled-components/pages/_document.js

但是这个例子使用了.js,我正在使用 Typescript。

我收到了一些类型错误和警告,我不确定如何正确输入。我已经解决了部分问题。这是我仍然缺少的:


错误 1:static async getInitialProps(ctx)函数的完整签名类型或返回类型应该是什么?ctx参数的类型是什么?

在此处输入图片说明


错误 2:对 的不安全访问ctx.renderPage。我想这将在我正确键入getInitialProps函数后修复

在此处输入图片说明


错误 3:这也可能与缺少的类型有关getInitialProps

在此处输入图片说明


import React, { ReactElement } from "react";
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
      originalRenderPage({
        enhanceApp: (App) => (props) =>
          sheet.collectStyles(<App {...props} />),
      });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }

  render(): ReactElement {
    return(
      <Html lang="en">
        <Head>
          // SOMETHING IN THE HEAD
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

ras*_*tay 22

分享我的使 _ document.tsx typescript 兼容的版本,特别是当您带着情感使用 Material UI 时。

_文档.tsx

import * as React from 'react'
import Document, {
  Html,
  Head,
  Main,
  NextScript,
  DocumentContext,
  DocumentInitialProps,
} from 'next/document'
import createEmotionServer from '@emotion/server/create-instance'
import { theme, createEmotionCache } from '@theme'
import { AppType } from 'next/dist/shared/lib/utils'
import { EmotionCache } from '@emotion/cache'

interface DocumentProps extends DocumentInitialProps {
  emotionStyleTags: React.ReactNode[]
}

const MyDocument = (props: DocumentProps) => {
  return (
    <Html lang="en">
      <Head>
        {/* PWA primary color */}
        <meta name="theme-color" content={theme.palette.primary.main} />
        <link rel="shortcut icon" href="/static/favicon.ico" />
        <link
          rel="stylesheet"
          href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
        />
        {/* Inject MUI styles first to match with the prepend: true configuration. */}
        {props.emotionStyleTags}
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with static-site generation (SSG).
MyDocument.getInitialProps = async (
  ctx: DocumentContext
): Promise<DocumentProps> => {
  // Resolution order
  //
  // On the server:
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. document.getInitialProps
  // 4. app.render
  // 5. page.render
  // 6. document.render
  //
  // On the server with error:
  // 1. document.getInitialProps
  // 2. app.render
  // 3. page.render
  // 4. document.render
  //
  // On the client
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. app.render
  // 4. page.render

  const originalRenderPage = ctx.renderPage

  // You can consider sharing the same emotion cache between all the SSR requests to speed up performance.
  // However, be aware that it can have global side effects.
  const cache = createEmotionCache()
  const { extractCriticalToChunks } = createEmotionServer(cache)

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (
        App: AppType | React.ComponentType<{ emotionCache: EmotionCache }>
      ) =>
        function EnhanceApp(props) {
          return <App emotionCache={cache} {...props} />
        },
    })

  const initialProps = await Document.getInitialProps(ctx)
  // This is important. It prevents emotion to render invalid HTML.
  // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153
  const emotionStyles = extractCriticalToChunks(initialProps.html)
  const emotionStyleTags = emotionStyles.styles.map((style) => (
    <style
      data-emotion={`${style.key} ${style.ids.join(' ')}`}
      key={style.key}
      // eslint-disable-next-line react/no-danger
      dangerouslySetInnerHTML={{ __html: style.css }}
    />
  ))

  return {
    ...initialProps,
    emotionStyleTags,
  }
}

export default MyDocument
Run Code Online (Sandbox Code Playgroud)

主题.ts

import { createTheme } from '@mui/material/styles'
import { red } from '@mui/material/colors'

// Create a theme instance.
const theme = createTheme({
  palette: {
    primary: {
      main: '#556cd6',
    },
    secondary: {
      main: '#19857b',
    },
    error: {
      main: red.A400,
    },
  },
})

export default theme
Run Code Online (Sandbox Code Playgroud)

创建EmotionCache.ts

import createCache from '@emotion/cache'

// prepend: true moves MUI styles to the top of the <head> so they're loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export default function createEmotionCache() {
  return createCache({ key: 'css', prepend: true })
}
Run Code Online (Sandbox Code Playgroud)

  • 我几个小时以来一直在努力反对这个问题。谢谢! (4认同)
  • 这确实应该是最佳答案。它是最简洁的,它真实地展示了您需要转换为 Typescript 以进行 MUI 缓存的内容。谢谢 shet_tayyy 你节省了我很多时间! (3认同)

cbd*_*per 7

这就是我最终为键入getInitialPropsrender方法所做的工作:

import React, { ReactElement } from "react";
import Document, { DocumentInitialProps, DocumentContext } from 'next/document';

export default class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
    // ...
  }

  render(): ReactElement {
    return(
      // ...
    );
  }

}

Run Code Online (Sandbox Code Playgroud)

https://github.com/vercel/next.js/blob/canary/examples/with-styled-components/pages/_document.js

完整styled-components示例:

import React, { ReactElement } from "react";
import Document, { Html, Head, Main, NextScript, DocumentInitialProps, DocumentContext } from 'next/document';
import { ServerStyleSheet } from "styled-components";

// NEXT.JS CUSTOM DOCUMENT
// https://nextjs.org/docs/advanced-features/custom-document

export default class MyDocument extends Document {
  
  static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
      originalRenderPage({
        enhanceApp: (App) => (props) =>
          sheet.collectStyles(<App {...props} />),
      });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }

  render(): ReactElement {
    return(
      <Html lang="en">
        <Head>
          // SOME HEAD ELEMENTS
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)