javascript - Gatsby 插件 : How Can I Take a Component as a Plug-In Option?

标签 javascript reactjs webpack gatsby

我正在尝试对现有的 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 的解决方案。
    于是,我发现了loadPageloadPageSync传递到插件中的函数......但那些也失败了。我尝试的每条路径都会导致组件返回......但它是一个“找不到页面”组件(可能是因为我试图传入的组件尚未添加为页面)。
    这似乎应该是一个简单的问题,至少对于以前使用过 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_modulesplugins目录 & 我们不会要求我们的用户将他们的自定义组件放入 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/

    相关文章:

    javascript - 使用 JQuery 处理切换的 onclick

    javascript - 在 React.js 中改变数据

    javascript - 将组件作为 props 传递的更好方法是什么?

    javascript - 如何在react.js中单击按钮时读取按钮中的值字段

    reactjs - react 如何在 redux-saga 中导航路由器?

    angular - 如何在节点模块包上跳过 typescript 的 noImplicitAny=true 规则?

    node.js - 如何代理 websocket 连接

    javascript - 如何在 webpack 中使用 babel-loader 选择性地处理 node_modules 中的代码?

    javascript - 设置变量返回函数值或 false

    javascript - 对话框最大化,最小化,调整大小,响应jquery ui js和扩展对话框js