在动态加载的 es 模块中使用 React

Posted

技术标签:

【中文标题】在动态加载的 es 模块中使用 React【英文标题】:Using React within a dynamically loaded es module 【发布时间】:2018-12-28 19:46:41 【问题描述】:

我一直在加载一个可以简化为src/test.tsx的原生ES模块:

export default class Test 
    constructor() 
        console.log('loaded');
    

我可以在我的浏览器中加载它并初始化它,非常棒:

import('http://127.0.0.1:8085/').then(m =>  
  const instance = new m.default();
);

但是..如果我想添加任何外部依赖项,在这种情况下是 React,我似乎无法弄清楚如何定位 es6 以及如何使用 tsc 捆绑 React。所以我的 dist 文件包含 import React from 'react'; 浏览器不知道如何解决,并产生:

(index):1 Uncaught (in promise) TypeError: Failed to resolve module specifier "react". Relative references must start with either "/", "./", or "../".

我的 tsconfig 看起来像:


    "compilerOptions": 
        "baseUrl": ".",
        "rootDir": "src",
        "module": "es6",
        "target": "es2015",
        "lib": ["es6", "dom"],
        "declaration": true,
        "jsx": "react",
        "outDir": "dist",
        "strict": true,
        "noImplicitAny": false,
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true,
        "experimentalDecorators": true,
        "moduleResolution": "node",
        "skipLibCheck": true
    ,
    "include": ["./src/**/*"]

运行节点 v9.5.0,打字稿 v2.9.2

我也尝试过使用 webpack 打包所有内容,但不知道如何以这种方式生成可动态导入的模块

【问题讨论】:

为什么要在浏览器中动态导入?您可以在代码中动态导入,webpack 会为您处理吗?想要更多? 【参考方案1】:

问题

在构建你的 typescript 文件之后,在你的组件顶部你将有一些如下的 import 语句(假设你的目标是 es2015)

import React from "react";

浏览器本身不知道如何解析“react”,因此它认为您将其误认为是另一个相对路径,例如 './react.js',因此报错

(index):1 Uncaught (in promise) TypeError: Failed to resolve module specifier "react". Relative references must start with either "/", "./", or "../".

目前有一个提案import-maps 将使浏览器能够原生支持这些导入语句。 Chrome 是 experimenting ,最终浏览器 javascript 将不得不提供一种方法来解析导入的模块。

解决办法

您可以做一个运行时解决方案或编译时解决方案:

运行时解决方案

我将使用systemjs,因为它已经被开箱即用的打字稿支持,你想将"module"更改为compilerOptions内的“系统”,比如

 "compilerOptions": 
   ....
   "module":"system"
  

这将使 typescript 编译为与 systemjs 兼容的代码,在您的根索引 html 文件中您想要包含 systemjs 文件,并且您还必须告诉 systemjs 在哪里寻找这些模块

// root index.html
 <script src="https://unpkg.com/systemjs@6.3.2/dist/system.js"></script>
 <script type="systemjs-importmap">
      
        // for each library you add u have to include it here
        // documentation is here [systemjs import maps][3]d
        "imports": 
          "react": "https://unpkg.com/react@16/umd/react.production.min.js",
          "react-dom": "https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"
        
      
  </script>

以后可以动态加载需要的模块

// load your module later
System.import("./dist/index.js").catch()

编译时解决方案

您可以使用 webpack、parcel 或任何其他 javascript 捆绑器,它们将在编译时为您解析这些模块,并为您提供一个捆绑包,其中已经捆绑了这些模块。

我推荐一个编译时解决方案,因为它为您提供的不仅仅是解析模块,您还可以使用 css 预处理器、css 模块、base64 内联图像等等。

【讨论】:

以上是关于在动态加载的 es 模块中使用 React的主要内容,如果未能解决你的问题,请参考以下文章

react之Lazy和Suspense(懒加载)

从 JSON 文件动态加载图像位置 - (找不到模块...) React, Next.js

react开发中如何使用require.ensure加载es6风格的模块

React 路由动态加载

ES6模块加载

React:在动态加载的 HTML 中注入组件的最佳方法?