如何动态地 execute/eval JavaScript 包含 ES6 模块/需要一些依赖项的代码?
How to dynamically execute/eval JavaScript code that contains an ES6 module / requires some dependencies?
我希望我的用户能够在我的 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) 使用 eval
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);
如果我未指定脚本类型或对选项 b) 使用 type="application/javascript"
,我将收到 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);
});
如果我使用 <source type="module">
在我的 index.html 中硬编码用户源代码,则会执行模块代码。动态加载模块代码似乎不起作用。我使用 Chrome 版本 63.0.3239.108.
=> 一、<script type="module">
标签动态添加到DOM后如何强制执行?或
=>二。我如何评估包含 import
(可能还有导出)语句的脚本?或
=> 三。允许用户源代码定义可以动态解析的依赖项的好方法是什么?
相关问题和文章:
Execute JavaScript code stored as a string
https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/#safely-sandboxing-eval
https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/
How do I include a JavaScript file in another JavaScript file?
Why is script.onload not working in a Chrome userscript?
补充说明:
我知道使用示例的工作流程,window.createTreeModel
并不理想。我在这里使用它是因为代码很容易理解。我将改进我的所有工作流程并考虑诸如安全问题之类的事情......在我以某种方式设法 运行 用户源代码包括其依赖项之后。
添加一些日志消息后,我发现使用 type="module"
:
$('body').append(script);
不执行模块代码
body.appendChild(script);
确实异步执行模块代码但是事件onload
和onreadystatechange
不工作,即使如果我使用 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
:
使用数据 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
导入在我的测试中有效,只是如果你使用相对路径,你可能必须更改目录更改前缀(如 ./ 或 ../),但由于你必须首先编码为文本,你可以在模拟之前用正则表达式替换它。
我希望我的用户能够在我的 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) 使用 eval
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);
如果我未指定脚本类型或对选项 b) 使用 type="application/javascript"
,我将收到 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);
});
如果我使用 <source type="module">
在我的 index.html 中硬编码用户源代码,则会执行模块代码。动态加载模块代码似乎不起作用。我使用 Chrome 版本 63.0.3239.108.
=> 一、<script type="module">
标签动态添加到DOM后如何强制执行?或
=>二。我如何评估包含 import
(可能还有导出)语句的脚本?或
=> 三。允许用户源代码定义可以动态解析的依赖项的好方法是什么?
相关问题和文章:
Execute JavaScript code stored as a string
https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/#safely-sandboxing-eval
https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/
How do I include a JavaScript file in another JavaScript file?
Why is script.onload not working in a Chrome userscript?
补充说明:
我知道使用示例的工作流程,window.createTreeModel
并不理想。我在这里使用它是因为代码很容易理解。我将改进我的所有工作流程并考虑诸如安全问题之类的事情......在我以某种方式设法 运行 用户源代码包括其依赖项之后。
添加一些日志消息后,我发现使用 type="module"
:
$('body').append(script);
不执行模块代码body.appendChild(script);
确实异步执行模块代码但是事件onload
和onreadystatechange
不工作,即使如果我使用 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
:
使用数据 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
导入在我的测试中有效,只是如果你使用相对路径,你可能必须更改目录更改前缀(如 ./ 或 ../),但由于你必须首先编码为文本,你可以在模拟之前用正则表达式替换它。