Next.js使用_document.js在body上自定义类



我在此处使用示例

https://github.com/zeit/next.js#custom-document

,我想更改身体标签上的custom_class

我曾尝试通过调用组件的道具,但无效。我想根据某种条件添加一个"黑暗"类,但我不知道如何更改。

编辑:

我不确定这是可能的。从NextJS Slack频道获得帮助后,我被告知

"Pages get rendered client side with next/link
And body is not touched after server side rendering"

我将尝试将内容包裹在我生成的另一个标签中,并尝试改变它。

我发现的最干净的解决方案不是声明性的,但效果很好:

import Head from "next/head"
class HeadElement extends React.Component {
    componentDidMount() {
        const {bodyClass} = this.props
        document.querySelector("body").classList.add(bodyClass || "light")
    }
    render() {
        return <Head>
            {/* Whatever other stuff you're using in Head */}
        </Head>
    }
}
export default HeadElement

使用此头部组件,您将进入"黑暗"或" Light"(遵循问题的示例光/黑暗主题(,作为页面中的BodyClass Prop。

截至当前下一个(10(和react(17(版本,如果您想从页面上更改身体类,则可以这样做:

// only example: maybe you'll want some logic before, 
// and maybe pass a variable to classList.add()
useEffect( () => { document.querySelector("body").classList.add("home") } );

请注意,使用效果是钩子,因此仅在现代功能组件而不是类的钩子中使用。

可以使用使用效果挂钩代替"旧" componentdidmount生命周期。

https://reactjs.org/docs/hooks-effect.htmlhttps://reactjs.org/docs/hooks-overview.htmlhttps://reactjs.org/docs/components-and-props.html

直接访问下一个js上body标签的唯一方法是通过_document.js文件,但是该文件是文档中所述的服务器端。

我建议的工作是直接从组件访问body标签。示例:

const handleClick = (e) => {
    document.querySelector('body').classList.toggle('dark')
}
<div onClick={handleClick}>Toggle</div>

不幸的是,next/head不允许像React头盔那样指定身体类:

<Helmet>
  <body className="foo"/>
</Helmet>

幸运的是,您可以在_document.js内使用this.props.__NEXT_DATA__.props.pageProps来访问页面道具,并使用它们在<body>元素上设置类:

import Document, { Html, Head, Main, NextScript } from 'next/document';
export default class MyDocument extends Document {
    static async getInitialProps(ctx) {
        const initialProps = await Document.getInitialProps(ctx);
        return { ...initialProps };
    }
    render() {
        const pageProps = this.props?.__NEXT_DATA__?.props?.pageProps;
        console.log('pageProps => ', pageProps);
        return (
            <Html>
                <Head />
                <body className={pageProps.bodyClassName}>
                    <Main />
                    <NextScript />
                </body>
            </Html>
        );
    }
}

更多信息。

我为我的情况提出了解决方案,而我不需要很多独特的身体类是创建一个称为bodyclass.js的组件,并将该组件导入我的layout.js。组件。

BodyClass.js

import  { Component } from 'react';
class BodyClass extends Component {
    componentDidMount() {
        if (window.location.pathname == '/') {
            this.setBodyClass('home');
            
        } else if (window.location.pathname == '/locations') {
            this.setBodyClass('locations');
        } 
    }
    setBodyClass(className) {
        // remove other classes
        document.body.className ='';
        // assign new class
        document.body.classList.add(className);
    }
    render() { 
        return null;
    }
}
 
export default BodyClass;

layout.js

import Header from './Header';
import Footer from './Footer';
import BodyClass from '../BodyClass';
const Layout = ({ children }) => {
    return (
        <React.Fragment>
            <BodyClass />
            <Header />
            {children}
            
            <Footer />
        </React.Fragment>
    )
}

export default Layout;

直接,这是不可能的。但是,通过另一种方式,您可以使用诸如tailwind之类的框架,然后将类直接插入CSS。
在这里使用tailwind的示例:

.dark {
  background-color: black;
  color: white;
}
body {
  @apply dark
}

这并不是您要寻找的答案,但是我能够通过通过组件传递道具来在功能上完成相同的事情,然后在顶级元素上更新一类包裹我的应用程序中的所有内容,并坐在下面。这样,每个页面都可以拥有自己的唯一类,并且操作与我使用身体类的方式相同。

这是我通过道具的地方

<Layout page_title={meta_title} page_desc={meta_desc} main_class={'contact_page'}>

这是我在布局组件中使用它的地方:

<main className={props.main_class}>
    <Header page_title={props.page_title} page_desc={props.page_desc} />
      {props.children}
    <Footer />
</main>

这是我的 CSS仅解决方案

(我首先查看https://stackoverflow.com/a/66358460/729221,但这感觉太复杂了,无法更改一些样式。(

(这使用了尾风CSS,但不需要。(

index.css:

body:has(#__next .set-bg-indigo-50-on-body) {
      @apply bg-indigo-50;
}

layout.tsx:

  //…
  return (
    <div className="set-bg-indigo-50-on-body">{children}</div>
  )

只要div<div id="__next">的直接父,这将起作用。否则,您需要更新CSS :has -RULE。

已经提到的答案是不必要的复杂,就像以下

一样简单

在下面创建一个CSS文件

src/styles/global.css

body {
  /* your styles here */
}

src/pages/_app.tsx中导入创建的CSS文件。

import '../styles/global.css'

最新更新