我正在尝试对现有的 Gatsby 插件进行改进,并且我想通过 gatsby-config.js
中的配置条目将 React 组件传递给插件。 :
plugins: [
{
resolve: `gatsby-plugin-modal-routing`,
options: { someComponent: SomeComponentClassOrFunction }
},
但是,我遇到的问题是我不知道如何使它工作。如果我尝试将组件本身作为插件配置的一部分传递,它似乎会与 JSON 进行序列化,从而导致类成为无用的对象。所以看来我必须传递一个路径字符串。
plugins: [
{
resolve: `gatsby-plugin-modal-routing`,
options: {
modalComponentPath: path.join(__dirname, 'src/components/SomeComponent.js')
}
},
但是,如果我尝试传递路径,我无法弄清楚如何使用它来加载插件内的组件。我尝试过使用动态节点导入(即 import(path).then(component => ...)
)...path.join
编辑 __dirname
src/components/SomeComponent
) ./src/components/SomeComponent
) .js
我不确定这是否是应用程序与插件的不同路径的某种问题,或者是否存在其他问题,但使用
import
无论如何,这似乎是一个不像 Gatsby 的解决方案。于是,我发现了
loadPage
和 loadPageSync
传递到插件中的函数......但那些也失败了。我尝试的每条路径都会导致组件返回......但它是一个“找不到页面”组件(可能是因为我试图传入的组件尚未添加为页面)。这似乎应该是一个简单的问题,至少对于以前使用过 Gatsby 插件的人来说:如果您希望插件将组件作为输入(作为函数/类或作为模块路径的字符串)......您如何在插件中实际使用该组件?
我正在寻找的只是一个基本模式或对现有 Gatsby 插件中的一行的引用,该插件采用一个组件,或者类似的简单东西(我可以查找任何细节)。
最佳答案
This seems like it should be a simple question
我自己尝试这个时也有同样的想法。好家伙。
TL:博士
// gatsby-node.js
const { DefinePlugin } = require('webpack')
const path = require('path')
exports.onCreateWebpackConfig = ({ actions }, { componentPath }) => {
actions.setWebpackConfig({
plugins: [
new DefinePlugin({
'___COMPONENT___': JSON.stringify(componentPath)
})
]
})
}
// gatsby-ssr
export const onRenderBody = ({ setPreBodyComponents }) => {
const Component = require(___COMPONENT___).default
setPreBodyComponents([<Component />])
}
长读
Gatsby 配置似乎没有传递函数(我可以发誓它曾经使用过),因此将 React 组件直接传递给您的自定义插件是不可能的。它必须是您的组件的路径。
// gatsby-config.js
{
resolve: 'my-custom-plugin',
options: {
componentPath: path.join(__dirname, './my-component.js')
}
}
你没有说你是否在使用 gatsby-node
中的组件或 gatsby-browser/ssr
,但我认为是后者,因为在 Node 中动态地要求东西非常简单:Gatsby 节点
// gatsby-node.js
function consume(component) {
const Component = require(component)
}
...虽然它不理解 JSX 或 ESM,但这是一个不同的问题。Gatsby 浏览器
gatsby-browser/ssr
是用 webpack 运行的,所以模块格式没有问题。但是import(componentPath)
不会工作:Dynamic expressions in import()
It is not possible to use a fully dynamic import statement, such as
import(foo)
. Because foo could potentially be any path to any file in your system or project.
webpack doc
好的,我想这样的事情应该可以工作:
// gatsby-browser
import('./my-dir' + componentPath)
不,因为 webpack 会尝试从插件所在的任何地方解决这个问题,即 node_modules
或 plugins
目录 & 我们不会要求我们的用户将他们的自定义组件放入 node_modules
.那这个呢?
// gatsby-browser
import(process.cwd() + componentPath) // nope
我们又回到了起点——webpack 不喜欢完整的动态路径!而且即使这样可行,这也是一个糟糕的主意,因为 webpack 会尝试捆绑整个工作目录。只有我们可以预先将路径编码为静态字符串,webpack 才能读取该代码——比如使用
webpack.DefinePlugin
定义环境变量。幸运的是,我们可以在 gatsby-node.js 中做到这一点:// gatsby-node.js
const { DefinePlugin } = require('webpack')
const path = require('path')
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
plugins: [
new DefinePlugin({
'___CURRENT_DIR___': JSON.stringify(process.cwd())
})
]
})
}
最后// gatsby-browser
// eslint throw error for unknown var, so disable it
// eslint-disable-next-line
import(___CURRENT_DIR___ + componentPath) // works, but don't do this
但是因为我们可以直接在 gatsby-node
中访问用户选项,让我们对整个路径进行编码: // gatsby-node.js
const { DefinePlugin } = require('webpack')
- const path = require('path')
- exports.onCreateWebpackConfig = ({ actions }) => {
+ exports.onCreateWebpackConfig = ({ actions }, { componentPath }) => {
actions.setWebpackConfig({
plugins: [
new DefinePlugin({
- '___CURRENT_DIR___': JSON.stringify(process.cwd())
+ '___COMPONENT___': JSON.stringify(componentPath)
})
]
})
}
回到 gatsby-browser.js:// gatsby-browser
// I pick a random API to test, can't imagine why one would import a module in this API
export const onRouteUpdate = async () => {
// eslint-disable-next-line
const { default: Component } = await import(___COMPONENT___)
console.log(Component) // works
}
Gatsby SSR为了完整起见,让我们在 gatby-ssr 中尝试相同的技巧:
// gatsby-ssr
export const onRenderBody = async ({ setPreBodyComponents }) => {
// const Component = require(___COMPONENT___).default
const { default: Component } = await import(___COMPONENT___)
setPreBodyComponents([<Component />])
}
......它失败了。Why? If one's curious enough they might go and dig around Gatsby code to see how gatsby-ssr is treated differently than gatsby-browser, but alas I just don't feel like doing that.
不要害怕,我们还有一招。 Webpack 的 require 也可以动态导入模块,但不是异步的。由于
gatsby-ssr
不在浏览器中运行,我不在乎异步性。export const onRenderBody = ({ setPreBodyComponents }) => {
const Component = require(___COMPONENT___).default
setPreBodyComponents([<Component />]) // works
}
现在它可以工作了。在 gatsby-ssr 和 gatsby-browser 之间共享代码
假设我们在
gatsby-ssr
中都需要这个组件和 gatsby-browser
——require(...)
在 gatsby-browser
工作也?export const onRouteUpdate = async () => {
// eslint-disable-next-line
const { default: Component } = require(___COMPONENT___)
console.log(Component) // yes
}
有用。import(..)
对比 require()
而import()
确实动态加载东西,它更像是一种代码拆分工具。除了异步之外,还有一些不同的地方:import('./my-dir' + componentPath)
将捆绑./my-dir
中的所有文件成 block 。我们可以使用魔术注释来排除/包含内容。require(...)
只会将所需的组件内联到调用它的任何 block 中。关于javascript - Gatsby 插件 : How Can I Take a Component as a Plug-In Option?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62972003/