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
运行它时它可以工作。
当我构建这个项目时,我得到了错误。我了解 document
、window
等在 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 && 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 的 graphql 查询中使用正则表达式
博客文章中的 Gatsby-js 客户端 javascript 内联
带有 Netlify CMS 的 Gatsby 图像 - 字段“图像”不能有选择,因为类型“字符串”没有子字段