在 React 中,如何检测我的组件是从客户端渲染还是从服务器渲染?

IT技术 javascript node.js reactjs
2021-04-24 03:31:27

我正在构建一个同构应用程序,但我使用的是仅在客户端上呈现的第三方组件。所以,特别是对于这个组件,我只需要在客户端渲染时渲染它。

如何检测我是在客户端还是在服务器?我正在寻找类似isClient()或的东西isServer()

4个回答

在内部,React 使用了一个ExecutionEnvironment为此调用的实用程序它实现了一些有用的属性,例如canUseDOMcanUseEventListeners解决方案基本上就是这里所建议的

实施 canUseDOM

var canUseDOM = !!(
  (typeof window !== 'undefined' &&
  window.document && window.document.createElement)
);

我像这样在我的应用程序中使用它

var ExecutionEnvironment = require('react/node_modules/fbjs/lib/ExecutionEnvironment');
...
render() {
  <div>{ ExecutionEnvironment.canUseDOM ? this.renderMyComponent() : null }</div>
}

编辑这是一个不应直接使用的未记录功能。它的位置可能会因版本而异。我通过展示 Facebook 团队内部使用的内容来分享这一点,以此表达“这是你能做的最好的事情”。您可能希望将此代码(它很小)复制到您自己的项目中,这样您就不必担心在不同版本之间保持其位置或潜在的破坏性更改。

另一个编辑有人为此代码创建了一个npm 包我建议使用那个。

npm install exenv --save

您可以使用 reacts lifecyle事件(例如:)componentDidMount来检测服务器/客户端渲染。

例子

作为钩

import { useState, useEffect } from 'react'

function useIsServer () {
  const [isServer, setIsServer] = useState(true)
  useEffect(() => {
    setIsServer(false)
  }, [])
  return isServer
}

用法

见下文(功能组件)

作为功​​能组件

import useIsServer from './above'

function ServerOnly ({ children = null, onClient = null }) {
  const isServer = useIsServer()
  return isServer
    ? children
    : onClient
}

用法

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

作为类组件

class ServerOnly extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      isServer: true
    }
  }

  componentDidMount() {
    this.setState({
      isServer: false
    })
  }

  render () {
    const { isServer } = this.state
    const { children, onClient } = this.props
    return isServer
      ? children
      : onClient
  }
}

用法

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

可能相关的两件事:

许多项目使用一些约定来设置全局 SERVER 或 CLIENT 布尔值,因此您的所有代码都可以基于它进行切换。在你的服务器包中,设置一些全局,就像在这个项目中一样

global.__SERVER__ = true;

并在您的客户端包中,将一些全局客户端设置为 true,您可以使用 Webpack 的 DefinePlugin 的一种方式来实现

new webpack.DefinePlugin({
  __CLIENT__: true
})

使用上述方法,您可以根据 willMount 或 render 中的变量进行切换,在服务器上做一件事,在客户端做另一件事。

在这里可能有用的第二件事是componentDidMount仅在客户端上运行,而不是在服务器上运行。

您也可以使用componentDidMount(),因为在服务器端呈现页面时不会运行此生命周期方法。