【问题标题】:Import a module from string variable从字符串变量导入模块
【发布时间】:2019-11-28 23:31:30
【问题描述】:

我需要从内存变量中导入一个 JavaScript 模块。 我知道这可以使用 SystemJSWebpack 来完成。

但是我在任何地方都找不到一个好的工作示例或相同的文档。文档主要讨论 .js 文件的动态导入。

基本上我需要像下面这样导入模块

let moduleData = "export function doSomething(string) { return string + '-done'; };"
//Need code to import that moduleData into memory

如果有人能指出很棒的文档

【问题讨论】:

  • 你的工具链中已经有 babel 了吗?如果是这样,我猜你已经有类似webpack.js.org/loaders/babel-loader 的东西,你可以在其中配置插件。现在,我没有找到任何适合您需求的插件。但是使用 babel-generator 解析和生成代码 (babeljs.io/docs/en/babel-generator) 和这个文档 github.com/jamiebuilds/babel-handbook 你可以编写自己的插件。
  • 这个Q有XY problem的感觉。多年来,我一直在使用带有 JavaScript 的模块加载器和模块捆绑器,并为相关项目做出了贡献,但我从来没有遇到过需要解决方案需要将字符串作为模块导入的问题。在某些情况下,我可以通过这样做来解决我的问题,但是有更好的 解决方案可用。虽然问题的当前编写方式,但我们不知道 XY 问题的 X 部分。为什么必须从包含模块源的字符串中导入模块?
  • 我同意@Louis。这是一个难以置信奇怪的问题,并解释为什么你需要这样做当然可以帮助你找到解决方案。
  • 基本上我们有一个 Angular 应用程序,我们让用户在其中创建 Angular 组件,包括 HTML 模板、TS 文件和 CSS。一旦他们输入了我们需要在同一个 Angular 应用程序中编译和加载的内容。我们已经弄清楚了如何将 HTML TS 和 CSS 合并编译成一个 JS 模块,现在加载部分是 let
  • 如果您需要完全在浏览器中执行此操作(正如您在 cmets 的答案中指出的那样),我认为这是不可能的。请记住,运行任意代码也是极其危险的。

标签: javascript angular typescript webpack systemjs


【解决方案1】:

import 语法存在一些限制,如果不使用外部库,即使不是不可能,也很难做到。

我能得到的最接近的是使用Dynamic Import 语法。一个例子如下:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<script>
    var moduleData = "export function hello() { alert('hello'); };";
    var b64moduleData = "data:text/javascript;base64," + btoa(moduleData);

</script>
<script type="module">

    async function doimport() {
      const module = await import(b64moduleData);
      module.hello();
    }

    doimport();

</script>

</body>
</html>

但是您会注意到,这对构建导入代码的方式有一些限制,这可能与您的需要不完全匹配。 最简单的解决方案可能是将模块的代码发送到服务器上,让它生成一个临时脚本,然后使用更传统的语法导入。

【讨论】:

    【解决方案2】:

    使用nodejs标志--experimental-modules来使用import ... from ...;

    node --experimental-modules index.mjs
    
    index.mjs
    import fs from 'fs';
    let fileContents = "export function doSomething(string) { return string + '-done'; };"
    let tempFileName = './.__temporaryFile.mjs';
    
    fs.writeFileSync(tempFileName, fileContents);
    console.log('Temporary mjs file was created!');
    
    import(tempFileName)
        .then((loadedModule) => loadedModule.doSomething('It Works!'))
        .then(console.log)
    
    


    延伸阅读here

    工作原理:

    1. 我先用fs.writeFileSync创建文件
    2. 然后我使用 import 方法的 promise 来加载模块和
    3. 管道 doSomething 方法调用与“It Works!”
    4. 然后记录 doSomething 的结果。

    致谢https://stackoverflow.com/a/45854500/3554534https://v8.dev/features/dynamic-import、@Louis

    【讨论】:

    • eval 是您的解决方案,不建议使用它。 w3schools.com/js/js_best_practices.asp 在这里阅读更多信息
    • eval = evil good explanation
    • 在处理执行模块代码时,反对“eval = evil”并不是一个好的反对。 SystemJS、Webpack、RequireJS 和我使用过的任何其他 JS 模块加载器的默认操作模式是使用与 eval 完全相同的权限运行代码。换句话说,require("foo") 并不比获取模块的源代码foo 然后eval 更安全。 一些模块加载器甚至将eval(sourceOfModule) 作为模块加载的一部分过程。话虽这么说,eval 不会处理 OP 的 export 语句,因此它足够做 OP 想要的。
    • 后期编辑,这个答案仍然没有达到目标。搜索“es6 动态导入”。静态导入可能会被提升,但动态导入不会被提升。我不认为实现动态导入以允许传递给import() 调用模块的源代码,但是可能是SystemJS 或Webpack 的插件允许它.即使这样的插件目前不存在,我相信可以开发一个。我不会称 OP 想要做的事情“不可能”。
    • @Louis 是的,你是对的。我通过谷歌搜索该消息得到了答案。我也会包括来源。谢谢!
    【解决方案3】:

    您可以即时创建组件和模块。但不是来自字符串。这是一个例子:

    name = "Dynamic Module on Fly";
    const tmpCmp = Component({
      template:
        '<span (click)="doSomething()" >generated on the fly: {{name}}</span>'
    })(
      class {
        doSomething(string) {
          console.log("log from function call");
          return string + "-done";
        }
      }
    );
    const tmpModule = NgModule({ declarations: [tmpCmp] })(class {});
    
    this._compiler.compileModuleAndAllComponentsAsync(tmpModule).then(factories => {
      const f = factories.componentFactories[0];
      const cmpRef = this.container.createComponent(f);
      cmpRef.instance.name = "dynamic";
    });
    

    这里是stackblitz

    【讨论】:

    • 不会工作 我们让用户也可以在 Fly 上创建模块
    • 基本上我们有一个 Angular 应用程序,我们让用户在其中创建 Angular 组件,包括 HTML 模板、TS 文件和 CSS。一旦他们输入了我们需要在同一个 Angular 应用程序中编译和加载的内容。我们已经弄清楚了如何将 HTML TS 和 CSS 合并编译成一个 JS 模块,现在剩下加载部分了
    【解决方案4】:
    let moduleData = await import("data:text/javascript,export function doSomething(str) { return str + '-done'; }");
    

    并进行测试

    moduleData.doSomething('test');
    

    【讨论】:

      【解决方案5】:
      <script type="module">
          import { myfun } from './myModule.js';
          myfun();
      </script>
      
      /* myModule.js  */
      export function myfun() {
          console.log('this is my module function');
      }
      

      【讨论】:

      • 那绝对不是动态的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2023-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多