我正在构建一个同构应用程序,但我正在使用一个仅在客户端渲染的第三方组件。因此,特别针对此组件,我需要仅在客户端渲染它。
如何检测当前是在客户端还是服务器端?我正在寻找类似于 isClient()
或 isServer()
的东西。
我正在构建一个同构应用程序,但我正在使用一个仅在客户端渲染的第三方组件。因此,特别针对此组件,我需要仅在客户端渲染它。
如何检测当前是在客户端还是服务器端?我正在寻找类似于 isClient()
或 isServer()
的东西。
在内部,React使用一个名为ExecutionEnvironment
的实用程序来处理这个问题。它实现了几个有用的属性,如canUseDOM
和canUseEventListeners
。本质上,解决方案就是这里所建议的。
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
!!
将值转换为布尔类型,以确保返回的结果是 true
或 false
而不是可能的 undefined
。您可以使用 Boolean
替换上面的 !!
,并获得相同的结果。 - Charlie Martin&&
运算符,我看不到任何可能会返回 undefined 或其他值而不是 true 或 false 的路径。我错了吗? - Jacopowindow.document
存在但没有createElement
方法,它将返回undefined
。如果它有一个createElement
方法,它将返回该方法,也就是一个函数。为了测试这个,你可以在你的javascript控制台中复制粘贴它(不包括!!
),并查看它是否返回一个函数。 - Charlie Martin您可以使用React的生命周期事件(例如: 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
})
您也可以使用componentDidMount()
,因为此生命周期方法不会在页面进行服务器端渲染时运行。
你也可以使用use-ssr
React 钩子函数
import useSSR from 'use-ssr'
const App = () => {
var { isBrowser, isServer } = useSSR()
// Want array destructuring? You can do that too!
var [isBrowser, isServer] = useSSR()
/*
* 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 ? '' : ''}
</>
)
}
window
是否已定义,因为在浏览器中它应该始终被定义。"最初的回答"var isBrowser = window!==undefined
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
);
if (typeof window === "undefined") { //client side code }
如果没有使用typeof
,你将会收到一个错误提示。
借助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
,它将在服务器端记录该函数已在客户端上调用。
(这是 TypeScript,将类型移除以获得 JS :))
window
或者process
这样的全局变量? - elclanrsif(windows) {}
,而实际上我应该使用typeof window
。 - Andre PenaReactDOM.render( <Component />, document.getElementById('root') , () => console.log('render!))
- Tony Jin