在 React 中,我如何检测我的组件是从客户端渲染还是从服务器渲染?
Posted
技术标签:
【中文标题】在 React 中,我如何检测我的组件是从客户端渲染还是从服务器渲染?【英文标题】:In React, how do I detect if my component is rendering from the client or the server? 【发布时间】:2015-11-19 21:19:21 【问题描述】:我正在构建一个同构应用程序,但我正在使用仅在客户端上呈现的第三方组件。所以,特别是对于这个组件,我只需要在客户端渲染时渲染它。
如何检测我是在客户端还是在服务器端?我正在寻找类似isClient()
或isServer()
的东西。
【问题讨论】:
你不能检查一些像window
或process
这样的全局变量吗?
类似:***.com/a/13644360/251311
谢谢@elclanrs 和@zerkms。这是我想到的第一件事,但我正在尝试if(windows)
,而实际上我应该这样做typeof window
。
ReactDOM.render( <Component />, document.getElementById('root') , () => console.log('render!))
【参考方案1】:
在内部,React 为此使用了一个名为 ExecutionEnvironment
的实用程序。它实现了一些有用的属性,例如canUseDOM
和canUseEventListeners
。解决方案基本上就是here 所建议的。
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 package。我建议使用它。
npm install exenv --save
【讨论】:
只是好奇,为什么!!一开始?!!
将值转换为布尔值,以确保返回 true
或 false
而不是可能的 undefined
。您可以将上面的 !!
替换为 Boolean
并获得相同的结果
由于这里我们使用&&
运算符,我看不到最终返回未定义或除真或假以外的其他值的路径。我错了吗?
如果window.document
存在,但没有createElement
方法,这将返回undefined
。如果它确实有一个createElement
方法,它将返回该方法又名一个函数。要对此进行测试,您可以将其复制并粘贴到您的 javascript 控制台中(不带 !!
),然后查看它是否返回了一个函数【参考方案2】:
可能相关的两件事:
许多项目使用一些约定来设置全局 SERVER 或 CLIENT 布尔值,因此您的所有代码都可以基于它进行切换。在你的服务器包中,设置一些全局的,like in this project
global.__SERVER__ = true;
在您的客户端包中,将一些全局客户端设置为 true,您可以通过一种方式实现with Webpack's DefinePlugin
new webpack.DefinePlugin(
__CLIENT__: true
)
通过上述方法,您可以在 willMount 或渲染中关闭该变量,以在服务器上做一件事,在客户端做另一件事。
这里可能有帮助的第二件事是componentDidMount
仅在客户端上运行,而不是在服务器上。
【讨论】:
能否请您查看我对此的回答?如有不妥之处,欢迎指正。【参考方案3】:在服务器元素层次结构的最顶层,可以添加ServerContext
,如下所示:
class ServerContext extends React.Component
getChildContext() return isServer: true ;
render() return React.Children.only(this.props.children);
ServerContext.propTypes =
children: React.PropTypes.node.isRequired,
;
ServerContext.childContextTypes =
isServer: React.PropTypes.bool.isRequired,
;
// Create our React application element.
const reactAppElement = (
<ServerContext>
<CodeSplitProvider context=codeSplitContext>
<ServerRouter location=request.url context=reactRouterContext>
<DemoApp />
</ServerRouter>
</CodeSplitProvider>
</ServerContext>
);
这样做,应该可以像这样从上下文中读取 isServer:
const Layout = (_, isServer ) => (
// render stuff here
);
【讨论】:
【参考方案4】:您也可以使用componentDidMount()
,因为页面在服务器端呈现时不会运行此生命周期方法。
【讨论】:
【参考方案5】:您可以在 exenv
包的帮助下创建一个有用的实用程序。
import canUseDOM from 'exenv';
export function onClient(fn: (..._args: any[]) => any): (..._args: any[]) => any
if (canUseDOM)
return fn;
if (process.env.NODE_ENV === 'development')
console.log(`Called $fn.name on client side only`);
return (): void => ;
并像这样使用它
function my_function_for_browser_only(arg1: number, arg2: string)
onClient(my_function_for_browser_only)(123, "Hi !");
并且该函数只会在客户端调用,如果设置NODE_ENV=development
,它会在服务器端登录该函数已在客户端调用
(这是打字稿,删除 JS 的类型 :))
【讨论】:
【参考方案6】:您可以使用 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'
/>
【讨论】:
【参考方案7】:你也可以只使用use-s-s-r
React 钩子
import uses-s-r from 'use-s-s-r'
const App = () =>
var isBrowser, isServer = uses-s-r()
// Want array destructuring? You can do that too!
var [isBrowser, isServer] = uses-s-r()
/*
* In your browser's chrome devtools console you should see
* > IS BROWSER: ?
* > IS SERVER: ?
*
* AND, in your terminal where your server is running you should see
* > IS BROWSER: ?
* > IS SERVER: ?
*/
console.log('IS BROWSER: ', isBrowser ? '?' : '?')
console.log('IS SERVER: ', isServer ? '?' : '?')
return (
<>
Is in browser? isBrowser ? '?' : '?'
<br />
Is on server? isServer ? '?' : '?'
</>
)
Example
【讨论】:
数组解构对我不起作用。我使用过对象解构。【参考方案8】:您可以检查是否定义了全局window
变量。
就像在浏览器中一样,它应该始终被定义。
var isBrowser = window!==undefined
【讨论】:
【参考方案9】:if (typeof window === "undefined") //client side code
没有typeof
,你会得到一个错误。
【讨论】:
以上是关于在 React 中,我如何检测我的组件是从客户端渲染还是从服务器渲染?的主要内容,如果未能解决你的问题,请参考以下文章