Gatsby:在使用带有 JavaScript 的 Bootstrap 组件构建项目时,服务器端渲染期间“文档”不可用

Posted

技术标签:

【中文标题】Gatsby:在使用带有 JavaScript 的 Bootstrap 组件构建项目时,服务器端渲染期间“文档”不可用【英文标题】:Gatsby: "document" is not available during server side rendering when building project using Bootstrap components with JavaScript 【发布时间】:2021-06-17 15:54:57 【问题描述】:

我有一个使用 Bootstrap(不是 React-Bootstrap,只是 Bootstrap)的 Gatsby 项目。我想在 javascript 中使用一些 Bootstrap 组件,但在构建项目时出现错误 "document" is not available during server side rendering

例如,我有以下组件 CollapseExample,它只有一个来自 Bootstrap 文档的 example。它还没有做任何事情,因为没有来自 Bootstrap 的 JavaScript。

// src/components/collapseexample.jsx
import * as React from "react";

export default function CollapseExample() 
  return (
    <React.Fragment>
      <a
        className="btn btn-primary"
        data-bs-toggle="collapse"
        href="#collapseExample"
        role="button"
        aria-expanded="false"
        aria-controls="collapseExample"
      >
        Link with href
      </a>
      <div className="collapse" id="collapseExample">
        <div className="card card-body">
          Some placeholder content for the collapse component. This panel is
          hidden by default but revealed when the user activates the relevant
          trigger.
        </div>
      </div>
    </React.Fragment>
  );


我有一个索引页面,其中只有一个 CollapseExample 的实例,如下所示:

// src/pages/index.Jsx
import * as React from "react";
import CollapseExample from "../components/collapseexample.tsx";

export default function IndexPage () 
  return <CollapseExample />;
;

我所做的是从 Bootstrap 导入 Collapse 并将 Collapse 添加到 CollapseExample 组件的状态,然后在 useEffect 挂钩中对其进行初始化,如下所示:

// src/components/collapseexample.jsx
import * as React from "react";
import  Collapse  from "bootstrap";

export default function CollapseExample() 
  /* Add Collapse component to state */
  const [collapse, setCollapse] = React.useState<Collapse>(null);

  /* Initialize Collapse component */
  React.useEffect(() => 
    if (collapse === null) 
      const collapseElement = document.getElementById("collapseExample");
      setCollapse(new Collapse(collapseElement));
    
  );

  /* Same as before */
  return (
    <React.Fragment>
      <a
        className="btn btn-primary"
        data-bs-toggle="collapse"
        href="#collapseExample"
        role="button"
        aria-expanded="false"
        aria-controls="collapseExample"
      >
        Link with href
      </a>
      <div className="collapse" id="collapseExample">
        <div className="card card-body">...</div>
      </div>
    </React.Fragment>
  );

我不知道这是否是正确的方法,因为我对 React 了解不多,但是当我使用 gatsby develop 运行它时它可以工作。

当我构建这个项目时,我得到了错误。我了解 documentwindow 等在 Node 中不可用,但 here 显示如下:

要解决此问题,请找到有问题的代码,然后 a) 在调用代码之前检查是否定义了窗口,因此代码在 Gatsby 构建时不会运行(请参阅下面的代码示例)或 b) 如果代码在渲染 React.js 组件的函数,将该代码移动到 componentDidMount 生命周期或 useEffect 钩子中,这样可以确保代码除非在浏览器中才能运行。

我使用document.getElementById("collapseExample") 的代码在useEffect 内,所以我不确定我是否遗漏或误解了某些内容,因为它在构建时不断产生相同的错误。

建筑物的输出如下所示:

  852 |
  853 |
> 854 | EventHandler.on(document, EVENT_CLICK_DATA_API$7, SELECTOR_DISMISS, Alert.handleDismiss(new Alert()));
      | ^
  855 | /**
  856 |  * ------------------------------------------------------------------------
  857 |  * jQuery


  WebpackError: ReferenceError: document is not defined
  
  - bootstrap.esm.js:854 
    [test-error]/[bootstrap]/dist/js/bootstrap.esm.js:854:1
  
  - bootstrap:19 
    test-error/webpack/bootstrap:19:1
...

谁能告诉我在 JavaScript 中使用 Bootstrap 组件并能够构建项目的正确方法是什么?

【问题讨论】:

【参考方案1】:

我在使用 bootstrap 5 时遇到了类似的问题。替换

import "bootstrap"

import "bootstrap/dist/css/bootstrap.min.css"

解决了我的问题。

【讨论】:

【参考方案2】:

添加以下条件:

  React.useEffect(() => 
    if (collapse === null && typeof document !== "undefined") 
      const collapseElement = document.getElementById("collapseExample");
      setCollapse(new Collapse(collapseElement));
    
  );

这是因为,本质上,gatsby build 发生在没有window or other global objects (such as document, because they are not even defined yet) 的服务器端,而gatsby develop 发生在客户端,因此您需要添加条件以避免在错误的一侧触发。这就是为什么它在 gatsby develop 上有效,但在 gatsby build 上无效。

我使用document.getElementById("collapseExample") 的代码是 在useEffect 里面,所以我不确定我是失踪还是 误解某事,因为它不断产生相同的错误 构建时。

您的 useEffect 没有依赖项(即使是一个空数组,[]),所以它会在状态的每个副作用上触发(例如,最初),这就是它破坏您的编译的原因。

在添加上述条件时,我强烈建议使用基于 React 的第三方依赖项(如 React-Boostsrap)来避免此类麻烦(可能会阻塞 React's hydration)。

【讨论】:

感谢您的回答。我添加了typeof document !== "undefined" 条件,但在构建时遇到了同样的错误。我评论了setCollapse(new Collapse(collapseElement));这一行并且它有效,所以我猜这意味着document是在调用document.getElementById("collapseExample");时定义的,但我不明白为什么它说document在调用new Collapse()时是未定义的下一行。我只想使用 Bootstrap,因为用 JavaScript 控制组件的样式和行为似乎更简单,但我想我会尝试 React-Bootstrap。 尝试使用以下条件:(collapse === null &amp;&amp; typeof window !== "undefined")【参考方案3】:

我在使用引导 toast 时遇到了类似的问题。虽然使用基于 React 的库总是更好,但如果你想使用普通的 Bootstrap,将以下代码添加到 gatsby-node.js 对我有用。

exports.onCreateWebpackConfig = ( stage, loaders, actions ) => 
 if (stage === "build-html" || stage === "develop-html") 
   actions.setWebpackConfig(
     module: 
       rules: [
         
           test: /bootstrap/,
           use: loaders.null(),
         ,
       ],
     ,
   );
  
;

这是我使用的 gatsby 调试博客的链接:https://www.gatsbyjs.com/docs/debugging-html-builds/#fixing-third-party-modules

【讨论】:

以上是关于Gatsby:在使用带有 JavaScript 的 Bootstrap 组件构建项目时,服务器端渲染期间“文档”不可用的主要内容,如果未能解决你的问题,请参考以下文章

在 Gatsby 中使用带有动态内容的网格模板区域

如何在带有 gatsby 的 graphql 查询中使用正则表达式

博客文章中的 Gatsby-js 客户端 javascript 内联

带有 Netlify CMS 的 Gatsby 图像 - 字段“图像”不能有选择,因为类型“字符串”没有子字段

样式化的组件,带有 gatsby-link 锚标签 css 着色

带有 Gatsby 的 Redux - 无法识别包装的提供程序