29

嗨,我正在尝试在我的 reactJS 项目中使用 react-rte。我有服务器端渲染,每次我想使用这个包时,我都会得到:

return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
                               ^
ReferenceError: window is not defined

我想问题可能出在同构工具上,但我不知道如何将包导入到已经定义窗口的客户端。

4

8 回答 8

22

这是一个可以为您处理窗口、文档和全局对象的 npm 库:Global

然后你可以安全地写:

import window from 'global'

const mySpecialWindowFunction = () => {
    return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
};
于 2017-10-24T10:36:20.017 回答
17

如果您正在执行服务器端渲染,则很有可能全局窗口对象将未定义,因为这只是客户端会理解的内容。

注意:最初,当您启动项目时,它会渲染出您的 DOM 的完整字符串(此时它不会知道,window因为它是服务器端,但随后使用您的客户端代码重新渲染窗口对象将可用!

在这种情况下,我正在使用一种解决方法。这就是我的 webpack 插件所拥有的:

new webpack.DefinePlugin({
  'process.env.NODE_ENV': isDevelopment ? '"development"' : '"production"',
  'process.env.BROWSER': JSON.stringify(true),
  __DEV__: isDevelopment
}),

所以我利用process.env.BROWSER了我的优势,因为它将被定义为undefined好像它是服务器端的,true如果客户端完成了渲染,它就会被定义。

由于当服务器端没有窗口对象时一切都停止工作,我们可以添加以下内容:

const mySpecialWindowFunction = () => {

  /* START HACK */
  if (!process.env.BROWSER) {
    global.window = {}; // Temporarily define window for server-side
  }
  /* END HACK */

  return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
};

这样,您的控制台就不会对您大喊大叫,也不会停止服务器端渲染,您现在可以继续您的辉煌一天!虽然我不得不承认这有点Hacky,但它完成了工作,因为我们要做的就是让服务器端渲染出初始 DOM 字符串,然后让客户端接管。

另请注意:不要担心将 window 设置为空对象,一旦客户端完成渲染,它将恢复正常。

于 2016-10-09T04:04:30.173 回答
11

对于其他试图弄清楚为什么他们的 SSR 失败的人,即使他们正在检查窗口对象是否未定义。

您需要检查窗口引用是否存在,而不是它的值是否为空!

  if (typeof window === 'undefined') console.log('Window is not there')

您的window && if (window)检查失败,因为在 JavaScript 中未声明和“未定义”是不同的东西。

const a = undefined;
if (a) console.log('a');  // logs nothing, since 'a' is undefined
if (b) console.log('b');  // ReferenceError: b is not defined

对原始问题的回答是,无论何时访问需要的“窗口”对象,都需要使它们有条件:

return (typeof window === 'undefined') ?
   'node' :
   /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());

于 2020-05-21T07:12:49.820 回答
3

我一直在使用 ReactJS.NET 目标构建到客户端和服务器,并且遇到了同样的问题。

解决方案是设置output.globalObject'this'.

module.exports = {
  // ...
  output: {
    // ...
    globalObject: 'this'
  }
};

如果没有设置该选项,它将回退到window正在处理仅限客户端的代码。

于 2019-12-11T12:43:11.697 回答
1

在进行服务器端渲染时,global likewindowdocument是未定义的。而且如果你想以同构的方式来做,你必须测试渲染组件时是什么环境。

https://github.com/DavidWells/isomorphic-react-example

github上有很多示例代码,上面的链接就是其中之一,希望对您有所帮助。

于 2016-08-15T08:34:48.330 回答
0

React 支持通过 'Suspense' 进行延迟加载,专门用于以下目的:

//add this to the top of your component
const RichTextEditor = React.lazy(() =>
  import("react-rte").then((module) => ({ default: module.Editor }))
);
render(){
     <Suspense fallback={<p>loading</p>}>
        <RichTextEditor />
     </Suspense>
}
于 2022-01-21T09:32:34.623 回答
0

我尝试在我的常量中将其设置为全局导入:

export const GLOBAL_WINDOW = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this;

在这种情况下,它会根据您在客户端应用程序或服务器上运行它的天气返回窗口或全局对象。

现在,您需要在要使用窗口对象的任何地方导入此常量。

前任:

import { GLOBAL_WINDOW } from '../constants/Constants';

const user = JSON.parse(GLOBAL_WINDOW.localStorage.getItem('user'));
于 2018-07-27T09:35:55.373 回答
-1

我发现的另一个解决方案是,您可以在“componentDidMount”事件中为您的“窗口变量”分配状态变量,并在“渲染”方法中测试您想要的状态变量是否为空或未定义,然后返回空,直到“componentDidMount”完成。

于 2019-11-29T16:39:00.740 回答