【问题标题】:Awaiting a dynamic import等待动态导入
【发布时间】:2018-09-17 06:55:13
【问题描述】:

我使用create-react-app 创建了一个带有 React 和 Typescript 的 Web 应用程序。它使用了一个相当繁重的第三方库。我想使用 dynamic import expressions 将其从主包中排除。

所以,我没有做import { Component } from 'library',而是创建了一个看起来像这样的小包装器:

const loadLibrary = async () => {
    const x = await import('library').then((r) => r);
    return x;
};
const {
    Component,
} = loadLibrary() as any;

// tslint:disable-next-line:no-console
console.log(`typeof Component is ${typeof Component}`);

export {
    Component,
};

然后,在我的应用程序中,我会使用 import { Component } from '../library-wrapper.ts'。 因为包装器使用动态导入表达式,Webpack 创建了一个单独的块,它只由浏览器下载,然后才是真正需要的。 \o/.

但坏消息是:我的包装器实际上并没有等待动态导入表达式。它调用loadLibrary() 函数但立即继续执行,记录

typeof 组件未定义

所以当我尝试使用Component 时,React 崩溃了:

元素类型无效:应为字符串(用于内置组件)或类/函数(用于复合组件)但得到:未定义。

有什么建议吗?

【问题讨论】:

    标签: reactjs typescript async-await


    【解决方案1】:

    loadLibrary 是一个异步函数,所以它返回一个 promise 而不是一个常规对象。因此,您必须通过await loadLibrary() 或loadLibrary().then(...) 来获取库对象。

    这样做的结果是您不能静态导出动态导入的内容,因为静态导入/导出是立即同步完成的,而动态导入是异步完成的。只能导出函数loadLibrary,让模块的用户在需要库的时候调用。

    简而言之,一次异步,永远异步;你不能强制异步的东西在 JavaScript 中同步运行。


    另外,顺便说一句,您的 loadLibrary 函数可以简化很多,只需

    const loadLibrary = () => import('library');
    

    因为 a) .then((r) => r) 只是创建了一个相同的承诺副本,b) 在异步函数中返回之前您不必等待(它是自动完成的)和 c) 无论如何都返回承诺的函数没有' t 必须标记为 async(尽管您仍然可以根据需要标记,例如为了可读性)。

    【讨论】:

    • 我认为async/await 的整个想法是您实际上可以等待 解决Promise。在这种情况下我错了吗?
    • 是的,你是对的,但是你仍然需要使用await 关键字来等待一个promise(告诉JS这是你想要的promise的结果而不是promise本身) .而且因为你不能以同步的方式运行异步代码(我相信这是由于 JavaScript 的单线程设计),你只能在标记为 async 的函数中使用 await,否则你将不得不使用.then(r => { /* do something with result */ })。
    【解决方案2】:

    我创建了一个接收名为 pdfName 的道具的 IIFE:

    const Component = ({ pdfName }) => {
    const [pdf, setPdf] = useState(undefined);
    
    (async () => {
        if (pdfName !== '') {
          await import(`../../assets/docs/${pdfName}.pdf`).then((r) => {
            setPdf(r.default);
          });
        }
      })();
      ...

    【讨论】:

      【解决方案3】:

      在我的例子中,应用程序因未定义的“弹出”、“确认”常量而崩溃。 使用 React Lazy 加载它 - 解决了这个问题!

      const Popup = lazy(() => import("./Popup"));
      const Confirm = lazy(() => import("./Confirm"));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-13
        • 1970-01-01
        • 2013-11-26
        • 2011-04-26
        • 1970-01-01
        相关资源
        最近更新 更多