webpack中动态加载外部模块失败

2024-05-18

我正在尝试建立以下架构:一个核心 React 应用程序,它具有一些基本功能,并且能够在运行时加载其他 React 组件。这些额外的 React 组件可以按需加载,并且它们在构建核心应用程序时不可用(因此它们不能包含在核心应用程序的捆绑包中,必须单独构建)。经过一段时间的研究,我发现Webpack 外部组件 https://webpack.js.org/configuration/externals/,这似乎很合适。我现在使用以下 webpack.config.js 单独构建我的模块:

const path = require('path');
const fs = require('fs');

process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';

const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

module.exports = {
  entry: './src/MyModule.jsx',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'MyModule.js',
    library: 'MyModule',
    libraryTarget: 'umd'
  },
   externals: {
    "react": "react",
    "semantic-ui-react": "semantic-ui-react"
   },
   module: {
    rules: [
        {
            test: /\.(js|jsx|mjs)$/,
            include: resolveApp('src'),
            loader: require.resolve('babel-loader'),
            options: {              
              compact: true,
            },
        }
    ]
  },
  resolve: {
    extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx']
  }
};

查看生成的 MyModule.js 文件,它对我来说看起来是正确的。

现在,在我的核心应用程序中,我按如下方式导入模块:

let myComponent = React.lazy(() => import(componentName + '.js'));

where componentName是与我的模块名称匹配的变量,在本例中,“MyModule”名称在构建时未知,并且该文件在构建时不存在于 src 文件夹中。为了避免在使用未知导入构建此代码时出现 webpack 错误,我已将以下内容添加到核心项目的 webpack.config.js 中:

module.exports = {
    externals: function (context, request, callback/*(err, result)*/) {
        if (request === './MyModule.js') {
            callback(null, "umd " + request);
        } else {
            callback();
        }
    }
}

我已经确认外部函数在构建期间被调用,并且 if 条件与该模块匹配。构建成功,我可以运行我的核心应用程序。

然后,为了测试动态加载,我删除了MyModule.js进入我的核心应用程序包所在的 static/js 文件夹,然后导航到我的核心应用程序中通过以下方式请求 MyModule 的页面let myComponent = React.lazy(() => import(componentName + '.js'));

我在导入行的控制台中看到运行时错误,

TypeError: undefined is not a function
    at Array.map (<anonymous>)
    at webpackAsyncContext 

我的猜测是找不到模块。我不明白它在哪里寻找模块,或者如何获取更多信息进行调试。


事实证明,我对 webpack 和动态加载做出了一些错误的假设。

我遇到了两件事的问题 - 我正在加载的模块类型以及我加载它的方式。

  1. 动态导入还不是标准的 ES 功能 - 它是将在 ES 2020 中标准化 https://github.com/tc39/proposal-dynamic-import/#import。此动态导入将only如果您尝试加载的模块对象是 ES6 模块(也称为包含“export ModuleName”的模块),则返回一个模块。如果您尝试加载打包为 CommonJS 模块、AMD、UMD 的内容,导入将会成功,但您将得到一个空对象。 Webpack 似乎不支持创建 ES6 格式的包 - 它可以创建各种模块类型,在上面的配置文件中,我实际上是在创建 UMD 模块(通过libraryTarget 设置进行配置)。

  2. 我对 import 语句本身有问题,因为我在 Webpack 捆绑的应用程序中使用它。 Webpack 重新解释了标准 ES 导入语句。在标准 webpack 配置(包括从 CRA 获得的配置)中,webpack 使用此语句作为包的分割点,因此即使是动态导入的模块也应该在 webpack 构建时存在(并且构建过程将失败,如果他们不可用)。我曾尝试使用 webpack 外部来告诉 webpack 动态加载模块,这使得构建能够在模块不存在的情况下成功。然而,该应用程序在运行时仍然使用Webpack的导入函数,而不是标准的JS导入函数。我通过尝试从浏览器控制台运行 import('modulename') 并得到与我的应用程序不同的结果来确认这一点,该应用程序与 webpack 捆绑在一起。

为了解决问题 #2,你可以通过在 import 语句中添加一些注释来告诉 Webpack 不要重新解释 ES 动态导入。

import(/*webpackIgnore: true*/ 'path/to/module.js');

这将阻止 Webpack 在构建时尝试查找和捆绑动态导入的模块,以及尝试在运行时导入它。这将使应用程序中的行为与浏览器控制台中的行为相匹配。

问题#1 有点难解决。正如我上面提到的,导入非 ES6 模块将返回一个空对象(如果您等待 Promise 或使用 .then())。然而,事实证明,文件本身确实加载并且代码被执行。您可以使用Webpack以“window”格式导出模块,然后按如下方式加载。

await import(/*webpackIgnore: true*/`path/to/module.js`);
let myModule = window['module'].default;

避免使用 window 对象的另一个潜在解决方案是使用能够生成 ES6 模块的系统(因此,不是 Webpack)构建模块。我最终使用 Rollup 创建了一个 ES6 模块,将所有依赖项提取到一个文件中,并通过 Babel 运行输出。这生成了一个通过动态 ES 导入成功加载的模块。以下是我的 rollup.config.js (请注意,我包含了模块中所需的所有外部节点模块 - 这使模块大小膨胀,但这是我的特定应用程序的要求 - 您的模块可能会有所不同,您需要配置 rollup 以排除模块)

// node-resolve will resolve all the node dependencies
import commonjs from 'rollup-plugin-commonjs';
import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import replace from 'rollup-plugin-replace';

export default {
  input: 'src/myModule.jsx',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    resolve(),
    babel({
      exclude: 'node_modules/**'
    }),
    commonjs({
      include: 'node_modules/**',      
      namedExports: {
        'node_modules/react/index.js': ['Children', 'Component', 'PropTypes',   'PureComponent', 'React', 'createElement', 'createRef', 'isValidElement', 'cloneElement', 'Fragment'],
        'node_modules/react-dom/index.js': ['render', 'createElement', 'findDOMNode', 'createPortal'],
        'node_modules/react-is/index.js': ['isForwardRef']
      }
    }),
    replace({
      'process.env.NODE_ENV': JSON.stringify( 'production' )
    })
  ]
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

webpack中动态加载外部模块失败 的相关文章

随机推荐

  • 如何使用 window.onerror 捕获所有 javascript 错误? (包括道场)

    这个问题是后续问题javascript 如何在弹出警报中显示脚本错误 https stackoverflow com questions 2604976 javascript how to display script errors in
  • 如何将图像和 POST 数据上传到 Azure 移动服务 ApiController 终结点?

    我正在尝试上传图片and POST表单数据 尽管理想情况下我希望它是json 到我的端点Azure 移动服务应用 我有ApiController method HttpPost Route api upload databaseId sea
  • 当实体的主键之间存在关系时,必须在调用 save() 之前手动分配此类的 ids

    我有两个实体 我想在它们之间创建一个关系 以便它们共享它们的主键 当我们提交一个实体时 另一个实体也应该使用为第一个实体生成的相同主键来提交 我的第一个实体是用户 Entity Table name ENDUSER public class
  • Activity 类型中的方法 showDialog(int) 在 Android 中已被弃用?

    方法showDialog int 从类型Activity is 已弃用 什么原因 以及如何解决 什么原因 http developer android com reference android app Activity html show
  • 模板中带有 ng-if 的 angularjs 指令

    我正在构建一个在模板内使用 ng if 的指令 奇怪的是 提供给链接函数的元素没有扩展ng if代码 它只是ng if的注释行 经过一番尝试 我发现通过将链接代码包装在 timeout 中似乎可以使其正常工作 但我想知道这是否不是正确的处理
  • Nginx docker容器代理传递到另一个端口

    我想在 docker 容器中运行 Nginx 它监听端口 80 并且当 url 以 word 开头时 我希望它 proxy pass 到端口 8080api 我有一些网络应用程序侦听端口 8080 这在没有 docker 的情况下对我来说一
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • Bigquery - 选择时间戳作为人类可读的日期时间

    如何在 Google Bigquery 中选择时间戳 存储为秒 作为人类可读的日期时间 schema id STRING signup date TIMESTAMP 我使用编写了一个查询DATE功能 但出现错误 SELECT DATE cr
  • 使用多个值过滤 JFX TableView

    我目前正在尝试过滤我的数据TableView using FilteredList with predicate 我有2个ComboBoxes来过滤值 我的表包含Result Each Result has a Student that S
  • 如何在Java媒体框架中学习.wav持续时间?

    我正在尝试使用 java 媒体框架将 mov 文件与 wav 文件合并 因此我需要知道它们的持续时间 我怎样才能做到这一点 任何想法 将不胜感激 您可以使用以下方式了解声音文件的持续时间 即 VitalyVal 的第二种方式 import
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • Excel:#CALC!使用 MAP 函数计算间隔重叠时出现错误(嵌套数组)

    我正在努力解决以下公式 它适用于某些情况 但不适用于所有情况 名字input有失败的数据集 得到一个 CALC 描述 嵌套数组 错误 LET input N1 0 0 N1 0 10 N1 10 20 names INDEX input 1
  • 在.rdlc报告的底部设置一个文本框

    我在 rdlc 报告中使用 tablix 有一个文本框 其中包含文本 签名 我想将此文本框放置在报告最后一页的底部 就在页脚之前 我已经用谷歌搜索了这个解决方案 但没有找到满意的结果 我的环境是VS2010 framework 4 0 有什
  • 内嵌显示定义术语和描述

    我正在为页面上的某些元素使用定义列表 并需要它们内联显示 例如 它们normally看起来像 我需要它们看起来像 注意多个 DD 我可以让它们在 moz 中使用 float 来正常工作 但无论我尝试什么 它们都无法在 IE 中工作 我通常会
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • 索引后文件被锁定

    我的 网络 应用程序中有以下工作流程 从存档下载 pdf 文件 索引文件 删除文件 我的问题是 对文件进行索引后 它仍然处于锁定状态 并且删除部分会引发异常 这是我用于索引文件的代码片段 try ContentStreamUpdateReq
  • 如何使用 pybrain 黑盒优化训练神经网络来处理监督数据集?

    我玩了一下 pybrain 了解如何生成具有自定义架构的神经网络 并使用反向传播算法将它们训练为监督数据集 然而 我对优化算法以及任务 学习代理和环境的概念感到困惑 例如 我将如何实现一个神经网络 例如 1 以使用 pybrain 遗传算法
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • 当按钮处于加载状态时,如何向按钮添加微调器图标?

    Twitter 引导按钮 http getbootstrap com javascript buttons有一个很好的Loading 状态可用 问题是它只显示一条消息 例如Loading 通过了data loading text像这样的属性
  • webpack中动态加载外部模块失败

    我正在尝试建立以下架构 一个核心 React 应用程序 它具有一些基本功能 并且能够在运行时加载其他 React 组件 这些额外的 React 组件可以按需加载 并且它们在构建核心应用程序时不可用 因此它们不能包含在核心应用程序的捆绑包中