动态加载的JavaScript模块缺少导出功能

时间:2020-06-03 00:25:40

标签: javascript node.js reactjs typescript webpack

我正在构建一个Web应用程序,该应用程序需要具有动态JavaScript模块的插件系统。为此,我使用的是ES6模块,该模块是通过调用import函数(见here)导入的。

在开始在当前项目中实现此功能之前,我做了一个较小的辅助项目以证明确实可行。此演示的代码如下。

// main_module.js - embedded in a script tag on the example skeleton page

import('/scripts/dyn_module.js')
  .then((m) => {
    console.log(m);
    m.init();
  })
  .catch((error) => console.log(`Unable to load module: ${error.message}`));
// dyn_module.js

export const init = () => {
  console.log('init function called!');
}

console.log('dynamic module loaded!');

这显示了预期的行为(即,dyn_module已加载,init脚本可见main_module函数)。

问题在于尝试在我的实际项目中使用它,该项目在Babel + webpack构建系统中使用TypeScript。将要从主模块中分别分别编译要加载的模块,然后由主模块基于配置文件import-插入(如上述测试用例)。该构建系统的配置如下所示,并且在编译示例模块或主模块时不会产生任何错误。

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true
  }
}
// .babelrc
{
  "presets": [
    "@babel/typescript",
    "@babel/react"
  ]
}
// webpack.config.js
const path = require('path');

module.exports = {
  mode: 'development',

  entry: {
    main: './src/main.tsx',
    'example.module': './src/modules/example.module.ts',
  },

  output: {
    path: path.resolve(__dirname, 'build/'),
    filename: '[name].js',
  },

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "babel-loader"
      },
      {
        test: /\.sass?$/,
        loader: 'style-loader!css-loader!sass-loader'
      }
    ]
  },

  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.sass']
  },

  // optimization: { usedExports: false }, // this didn't have any effect on the module exports visibility
}

然后,主模块使用此行代码导入示例模块,该模块已由我为该项目构建的示例Web服务器成功解析。

// ./src/main.tsx

// ...

    const modLoadOps: Array<Promise<Object>> = [];

    props.config.mods.forEach((mod: ModTypes.StandardModule) => {
      modLoadOps.push(
        // Eval here, as webpack will try to bundle this module with the main module which defeats the point of the plugin system (and webpack's lazy loading config comment throws errors at compile time)
        eval(`import('/scripts/modules/${mod.scriptName}')`)
      );
    });

    Promise.all(modLoadOps)
      .then((mods: Array<Object>) => {
        // Debugging ... objects are always empty
        console.log(mods);

        // This line creates an array of undefined objects, as it can't utilize any exports
        const loadedMods: Array<ModTypes.StandardModule> =
          [...(mods as Array<{ Mod: ModTypes.StandardModule }>).map(v => v.Mod)];

        // This is called, but ends up failing (which returns to the catch handler below), as it can't utilize the module's internal class implementation
        this.finishedLoading(loadedMods);
      })
      .catch((reason) => {
        console.log((reason as Error).message);
        this.failedLoading();
      });

// ...
// example.module.ts
import * as ModTypes from '../types/module.types';

export default class Mod implements ModTypes.StandardModule {
  public entry(): void {
    console.log('[Example Module] entry function called!');
  }
}

如代码注释中所述,模块(均为example.module.ts副产品)在正确加载的情况下,从不显示对象属性中的导出内容。

![图像:上面代码中的调试消息的控制台输出]( https://i.imgur.com/E68yph3.png

在尝试通过以下类似方法打印对象的属性时,也是如此。

mods.forEach(mod => console.log(Object.keys(mod));

有人会对此有何想法吗?如何在不丢失TypeScript提供的类型安全性的情况下,使动态加载的模块的导出内容可以被父脚本访问?

谢谢您的时间。

0 个答案:

没有答案