Razzle: окно не определено

Созданный на 11 февр. 2020  ·  14Комментарии  ·  Источник: jaredpalmer/razzle

Всем привет,

Я столкнулся с проблемой «окно не определено» при попытке запустить файл «server.js». Ошибка выглядит следующим образом:

$ NODE_ENV=production node build/server.js
/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/editorOptions.js:31
    if (window.ace) {
    ^

ReferenceError: window is not defined
    at Object.getAceInstance (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/editorOptions.js:31:5)
    at Object.<anonymous> (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/ace.js:31:27)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.<anonymous> (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/index.js:3:13)

Когда я пытаюсь использовать пакет @elastic/eui в своем приложении.

Действия по воспроизведению:

  1. Создать новое приложение
npx create-razzle-app my-app
  1. Устанавливает необходимые пакеты
cd my-app
yarn add @elastic/eui @elastic/datemath moment
  1. Откройте Home.js и замените содержимое, как показано ниже.
import React from 'react';
import logo from './react.svg';
import './Home.css';
import { EuiFieldPassword } from '@elastic/eui';

class Home extends React.Component {
  render() {
    return <div className="Home">
            <div className="Home-header">
                <img src={logo} className="Home-logo" alt="logo" />
                <h2>Welcome to Razzle</h2>
            </div>
            <p className="Home-intro">
                To get started, edit <code>src/App.js</code> or <code>src/Home.js</code> and save to reload.
            </p>
            <ul className="Home-resources">
                <li>
                    <a href="https://github.com/jaredpalmer/razzle">Docs</a>
                </li>
                <li>
                    <a href="https://github.com/jaredpalmer/razzle/issues">Issues</a>
                </li>
                <li>
                    <a href="https://palmer.chat">Community Slack</a>
                </li>
            </ul>
            <EuiFieldPassword placeholder="Placeholder text" value={'helllow there'} onChange={() => {}} aria-label="Use aria labels when no actual label is in use" />
        </div>;
  }
}

export default Home;

  1. Строить
yarn build
  1. Запустите server.js
yarn start:prod

Ожидается: запустится успешно без проблем
Фактически: получение «окно не определено»

Я знаю, что объект «окно» или «документ» не будет использоваться или доступен в Node. Итак, есть ли способ решить эту проблему?

Спасибо

Самый полезный комментарий

const ssrEuiFieldPassword =
  process.env.BUILD_TARGET === 'client' ? require('@elastic/eui').EuiFieldPassword : OtherComponent;

Все 14 Комментарий

Вы визуализируете серверную часть редактора форматированного текста. Это проблема @elastic/eui или react-ace.

@fivethreeo понял. И Razzle поддерживает SSR по умолчанию, так что это также означает, что Razzle не будет работать, если мое приложение использует библиотеку расширенного текстового редактора?
Кстати, поскольку мое приложение в настоящее время использует CRA с рендерингом на стороне клиента, и я думаю перейти на Razzle для SSR.

Вы можете сделать что-то еще на стороне сервера.

@fivethreeo , у вас есть предложения, как это сделать? Поскольку все будет отображаться под <App /> .

Насколько я знаю, любые библиотеки/компоненты, которые я использовал на стороне клиента, будут отображаться в коде сервера.

` ReactDomServer.renderToString( <StaticRouter context={context} location={req.url}> <App /> </StaticRouter> );

const ssrEuiFieldPassword =
  typeof window !== 'undefined' ? EuiFieldPassword : OtherComponent;

@fivethreeo , кажется, не работает, я добавил так:

class Home extends React.Component {
    render() {
      const ssrEuiFieldPassword = typeof window !== 'undefined' ? EuiFieldPassword : div;
      return <div className="Home">
                <div className="Home-header">
                    <img src={logo} className="Home-logo" alt="logo" />
                    <h2>Welcome to Razzle</h2>
                </div>
                <p className="Home-intro">
                    To get started, edit <code>src/App.js</code> or <code>src/Home.js</code> and save to reload.
                </p>
                <ul className="Home-resources">
                    <li>
                        <a href="https://github.com/jaredpalmer/razzle">Docs</a>
                    </li>
                    <li>
                        <a href="https://github.com/jaredpalmer/razzle/issues">Issues</a>
                    </li>
                    <li>
                        <a href="https://palmer.chat">Community Slack</a>
                    </li>
                </ul>
                <ssrEuiFieldPassword placeholder="Placeholder text" value={'helllow there'} onChange={() => {}} aria-label="Use aria labels when no actual label is in use" />
            </div>;
    }
  }

  export default Home;

Я мог успешно запустить приложение, но столкнулся с проблемой при доступе к http://localhost :3000

$ NODE_ENV=production node build/server.js
🚀 started
(node:8236) UnhandledPromiseRejectionWarning: ReferenceError: window is not defined
    at Object.getAceInstance (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/editorOptions.js:31:5)
    at Object.<anonymous> (/Users/sanguyen/Documents/Work/my-app/node_modules/react-ace/lib/ace.js:31:27)

Я бы сделал это вне компонента, чтобы вы не переопределяли его при каждом рендеринге/монтировании.

Пытаться

const ssrEuiFieldPassword =
  process.env.BUILD_TARGET === 'client' ? EuiFieldPassword : OtherComponent;

О, неправильно прочитал этот след. Вам нужно выполнить условный импорт/требовать.

const ssrEuiFieldPassword =
  process.env.BUILD_TARGET === 'client' ? require('@elastic/eui').EuiFieldPassword : OtherComponent;

Спасибо @fivethreeo , все работает!

@samjetski хорошо, если вы это сделаете, вы получите некоторые ошибки гидратации в консоли браузера, согласно документам ReadDOM :

Если вам намеренно нужно отображать что-то другое на сервере и на клиенте, вы можете выполнить двухпроходный рендеринг. Компоненты, которые отображают что-то другое на клиенте, могут считывать переменную состояния, такую ​​как this.state.isClient, для которой вы можете установить значение true в componentDidMount(). Таким образом, начальный проход рендеринга будет отображать тот же контент, что и сервер, избегая несоответствий, но дополнительный проход будет выполняться синхронно сразу после гидратации. Обратите внимание, что этот подход сделает ваши компоненты медленнее, потому что они должны рендериться дважды, поэтому используйте его с осторожностью.

поэтому я предлагаю создать компонент с именем NoSSR следующим образом:

class NoSSR extends React.Component {
  state = {
    isClient: false
  }
  componentDidMount() {
     this.setState({ isClient: true })
  }
  render() {
    const { isClient } = this.state
    const { children } = this.props
    return isClient ? children : null;
  }
}

и используйте его таким образом:

<NoSSR>
  <EuiFieldPassword 
      placeholder="Placeholder text" 
      value={'helllow there'} 
      onChange={() => {}} 
      aria-label="Use aria labels when no actual label is in use" 
    />
</NoSSR>

ГГ ЭЗ

Пожалуйста, закройте эту проблему после прочтения этого сообщения.

Закрыт из-за бездействия. Крикните, если это ошибка, и мы снова откроем его.

поэтому я предлагаю создать компонент с именем NoSSR следующим образом:

class NoSSR extends React.Component {
  state = {
    isClient: false
  }
  componentDidMount() {
     this.setState({ isClient: true })
  }
  render() {
    const { isClient } = this.state
    const { children } = this.props
    return isClient ? children : null;
  }
}

@ nimaa77 Мне нравится ваше действительно хорошее решение для отключения SSR.

Решение состоит в том, чтобы отключить SSR для вашего компонента react-ace. Ни один из приведенных примеров не работал для меня.

Фрагмент ниже работает в NextJS v10.0.3. Обратите внимание, что использование require вместо import ; ваш линтер, вероятно, отметит это.

import React from "react";
import dynamic from "next/dynamic";

const Editor = dynamic(
    async () => {
        const ace = await require("react-ace");
        require("ace-builds/src-noconflict/mode-javascript");
        require("ace-builds/src-noconflict/theme-github");
        return ace;
    },
    {
        loading: () => (
            <p>Loading</p>
        ),
        ssr: false,
    },
);

export default function CodeEditor(props) {
    return (
        <Editor
            {...props}
        />
    );
}

...и затем используйте его, где хотите, так же, как если бы вы делали это с AceEditor:

<CodeEditor
    onChange={(text )=> console.log(text)}
    mode="javascript"
    theme="github"
    showPrintMargin={false}
    width="100%"
    editorProps={{ $blockScrolling: true }}
/>

Я понимаю, что это репо не имеет прямого отношения к react-ace, но эта проблема в настоящее время является лучшим результатом в Google для этого сообщения об ошибке. Надеюсь, это поможет кому-то еще.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

panbanda picture panbanda  ·  5Комментарии

jcblw picture jcblw  ·  4Комментарии

krazyjakee picture krazyjakee  ·  3Комментарии

knipferrc picture knipferrc  ·  5Комментарии

charlie632 picture charlie632  ·  4Комментарии