6

我希望我的用户能够在我的 JavaScript 应用程序中使用 JavaScript 作为脚本语言。为此,我需要动态执行源代码。

动态执行 JavaScript 似乎有两个主要选项:

a) 使用eval(...)方法(或var func = new Function(...);)。

b)<script>向 DOM 添加一个节点(例如使用$('body').append(...))。

只要我不在import动态执行的源代码中使用任何语句,这两种方法都可以正常工作。如果我包含import语句,我会收到错误消息Unexpected identifier

要执行的示例用户源代码:

import Atom from './src/core.atom.js':

window.createTreeModel = function(){
   var root = new Atom('root');
   root.createChildAtom('child');
   return root;
}

示例应用程序代码来说明该动态代码的可能用法:

a) 使用评估

var sourceCode =  editor.getText(); 
window.createTreeModel = undefined;
eval(sourceCode);
var model = window.createTreeModel();
treeView.setModel(model);

b) 使用 DOM 修改:

var sourceCode =  editor.getText(); 
window.createTreeModel = undefined;

var script = "<script >\n"+ 
            sourceCode + "\n" +             
            "</script>";

$('body').append(script); 

var model = window.createTreeModel();
treeView.setModel(model);

type="application/javascript"如果我没有为选项 b) 指定脚本类型或使用,我会收到Unexpected identifier错误消息。如果我使用type="module"我不会出错。脚本标签成功添加到 DOM,但模块代码没有执行。

我首先认为这可能是由于异步加载。但是,等到脚本标签的加载完成并不能使用type='module'. 加载机制可以使用,type="application/javascript"但是......再次......import不起作用。

加载脚本标签后异步执行的示例代码:

function loadScript(sourceCode, callback){
        // Adding the script tag to the head as suggested before
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.type = 'application/javascript';
        script.innerHTML = sourceCode;
        //script.async=false;

        // Then bind the event to the callback function.
        // There are several events for cross browser compatibility.
        script.onreadystatechange = callback;
        script.onload = callback;

        // Fire the loading
        head.appendChild(script);
    }

--

loadScript(sourceCode, function(){
        var model = window.createModel();
        console.log('model:' + model);
     });  

如果我使用 对 index.html 中的用户源代码进行硬编码<source type="module">,则会执行模块代码。动态加载模块代码似乎不起作用。我使用 Chrome 版本 63.0.3239.108。

<script type="module">=> I.动态添加到 DOM 后如何强制执行标签?或者

=> 二。如何评估包含import(可能还有导出)语句的脚本?或者

=> 三。允许用户源代码定义可以动态解析的依赖关系的好方法是什么?

相关问题和文章:

进一步说明:

我知道使用示例的工作流程window.createTreeModel并不理想。我在这里使用它是因为代码易于理解。在我设法以某种方式运行用户源代码(包括其依赖项)之后,我将改进我的所有工作流程并考虑诸如安全问题之类的问题。

4

2 回答 2

4

添加一些日志消息后,我发现使用时type="module"

  • $('body').append(script);不执行模块代码

  • body.appendChild(script); 确实异步执行模块代码,但事件onloadonreadystatechange不起作用,即使我使用 addEventListener(...) 而不是script.onload =....

以下工作对我有用。它修改用户源代码以包含对(时间)全局回调的调用:

    var sourceCode =  editor.getText(); 

    window.scriptLoadedHook = function(){
        var model = window.createTreeModel();
        console.log('model:' + model);
        window.scriptLoadedHook = undefined;
    };

    var body = document.body;
    var script = document.createElement('script');
    script.type = 'module';
    script.innerHTML = sourceCode + "\n" + 
                       "if(window.scriptLoadedHook){window.scriptLoadedHook();}";           
    body.appendChild(script);   

我现在尝试找出如何使用<script type="module">标签中的导出来至少摆脱全局函数window.createModel

如何导入已在 html 中的 <script type="module"> 标签中定义的 es6 模块?

于 2017-12-26T15:04:57.963 回答
3

使用数据 uris 或 objectUrls 和动态导入:

数据URI:

const code = 'export default function hello() { console.log("Hello World"); }';
const dataUri = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(code);
const module = await import(dataUri);
console.log(module); // property default contains function hello now
const myHello = module.default;
myHello(); // puts "Hello World" to console

对象网址:

const code = 'export default function hello() { console.log("Hello World"); }';
const objectURL = URL.createObjectURL(new Blob([code], { type: 'text/javascript' }));
const module = await import(objectURL);
console.log(module); // property default contains function hello now
const myHello = module.default;
myHello(); // puts "Hello World" to console

导入在我的测试中起作用,只是如果您使用相对路径,您可能必须更改目录更改前缀(如 ./ 或 ../),但由于您必须首先将其编码为文本,您可以在之前用正则表达式替换它模仿。

于 2021-05-02T17:42:18.577 回答