【问题标题】:import file dynamically by variable - react native通过变量动态导入文件 - 反应原生
【发布时间】:2020-06-19 02:15:39
【问题描述】:

我有一个包含组件路径的 path.json 文件

// path.json

{
  "main": "./login/index.js",
  "paths": [
    {
      "name": "login",
      "path": "./login/index.js",
      "image": ""
    }
  ]
}

我想在 react native 中动态加载 './login/index.js' 文件并渲染这个特定文件

我目前的实现

const MyComponent = createLazyContainer(() => {
  const componentPath = PathJson.main; // ./login/index.js
  return import(`${componentPath}`); //import error here @ line 7
});

export default MyComponent;

我收到以下错误:

第 7 行调用无效:import("" + componentPath)

【问题讨论】:

  • 您可以使用动态导入,但在动态导入时必须使用真实字符串而不是变量或模板。
  • @JoeLloyd Metro bundler 插件有什么办法吗?
  • 我不知道那个插件是什么。但是动态导入非常先进,如果您确实需要它们,请不要这样做。主要用于高级代码拆分。

标签: reactjs react-native ecmascript-6 metro-bundler


【解决方案1】:

人们在帖子中告诉您的内容是正确的,但我想添加一种可能的解决方案。所有导入/要求都在编译时解决,而不是在您尝试执行的运行时解决。在您运行应用程序时,如果您尚未导入文件,则无法使用它们。

有一个解决方法,假设您事先知道所有文件,您可能会做一些类似工厂的事情:

   const possiblePaths = {
     'one': require('path/to/file/1),
    'two': require('path/to/file/2)
}

funtion(type){
    return possiblePaths[type]
}

然后你以某种方式使用它:

render(){
   const MyComponent = function('one')

  return <MyComponent/>
}

这或多或少是伪代码,我不能马上工作,但希望你能明白。您需要存储对您可能需要的每个导入的引用,然后不要使用导入,而是使用在编译时为您创建的引用。

【讨论】:

    【解决方案2】:

    在 React Native 中,所有正在导入的文件都捆绑在一起,只有这些文件可以动态导入。

    假设你有三个文件 index.jstest_1.jstest_2.js,如果你在 index.js 中只导入了 test_1.js,那么 React Native 只会捆绑这两个文件离开 test_2.js

    因此,即使动态导入在 React Native 中有效,但要回答您的问题,但由于这些文件不是捆绑包的一部分,您无法导入它们。

    【讨论】:

    • 因此,如果确实在索引中导入 test_1.js 和 test_2.js,但不使用它们,那么它们将被捆绑。如果然后尝试导入(${componentPath} 其中组件路径是 test_2.js 的路径,那么不会有错误并且动态导入会成功?
    • 是的,metro bundler 需要知道将在编译时使用的所有文件。
    【解决方案3】:

    实际上,React Native 开发关注点不像 Web 开发。

    正因为如此,在 react-native 项目的生产中 lazy 加载一点都不重要。只需导入您想要的任何内容,然后在项目的任何文件中使用它们。它们都在生产包中,完全不重要。

    所以对于这个问题,我更喜欢有一个帮助文件来收集所有可选择的库并导出它们:

    // helper file
    export { default as Index } from './Login';
    export { default as OtherComponent } from './OtherComponent';
    

    那么当你想使用时:

    import { Index, OtherComponent } from 'helper';
    
    ~~~
    
    render() {
      const MyComponent = someCondition ? Index : OtherComponent;
    
      return (
        <MyComponent />;
      );
    }
    

    【讨论】:

      【解决方案4】:

      解决方案:

      const allPaths = {
        path1: require('file path1').default,
        path2: require('file path2').default
      };
      
       render(){
        const MyComponent = allPaths["path1"];
      
        return <MyComponent/>
       }
      
      
      

      【讨论】:

        【解决方案5】:

        我曾经遇到过类似的情况,我需要通过变量进行导入,但这仅限于在组件内导入组件并且它使用代码拆分 (编辑:我正在寻找不依赖代码拆分的解决方案,我刚刚意识到问题中有一个 react-native 标签,我不认为代码拆分是一个好的选择在注册护士中)。我不确定我的方法对你有多大帮助,但这里有。

        旁注:

        • 包含index.js(jsx|ts|tsx) 文件的导入文件夹应自动解析为该index 文件。
        • from './login/index.js' 导入通常会引发'Module not found' 错误。导入from './login/index'from './login,但我更喜欢最后一个,因为它最短且最简单。


        path.json:

        {
          "main": "./login", // '.js' is removed
          "paths": [
            {
              "name": "login",
              "path": "./login/index.js", // Not sure what this is for, but if necessary, remove the '.js' here as well
              "image": ""
            }
          ]
        }
        


        MyComponent.js:

        import React, { lazy, Suspense } from 'react'
        import PathJson from './path'
        
        // 1. We need a UI to show while component is being loaded
        const Loader = () => <div>{'Loading...'}</div>
        
        // 2. We need a fallback UI if component is not found
        const DocUnavailable = () => <div>{'We\'re sorry, but this document is unavailable.'}</div>
        
        // 3. Create a resolver function ('resolver' is just a name I give)
        function resolveImport(pathToComponent, FallbackComponent) {
          let componentFound = false
          let RenderComponent = () => <FallbackComponent /> // Assign fallback first
          try {
            if (require.resolve(pathToComponent)) {
              componentFound = true
            }
          } catch (e) { } // Kinda hacky, if you don't mind, but it works
          if (componentFound) {
            // If found, replace fallback with the valid component
            RenderComponent = lazy(() => import(pathToComponent))
          }
          return RenderComponent
        }
        
        // 4. Finally, implement it in a component
        class MyComponent extends React.Component {
        
          render() {
            const componentPath = PathJson.main
            const RenderComponent = resolveImport(componentPath, DocUnavailable)
            return (
              <Suspense fallback={<Loader />}>
                <RenderComponent />
              </Suspense>
            )
          }
        
        }
        
        export default MyComponent
        


        参考资料:

        【讨论】:

          猜你喜欢
          • 2020-03-27
          • 2021-02-18
          • 2017-09-24
          • 1970-01-01
          • 2023-04-01
          • 2021-10-05
          • 1970-01-01
          • 2018-08-23
          • 1970-01-01
          相关资源
          最近更新 更多