在前面的第4节中,已经介绍了如何生成并执行js代码,本篇将补充一些其它的内容。
1. 基本步骤
第一步:引入js库
<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>
第二步:调用workspaceToCode方法生成js代码
Blockly.JavaScript.addReservedWords('code'); // 1
var code = Blockly.JavaScript.workspaceToCode(workspace);
注意: 上面的第1句代码将code
其添加到保留字列表中,以便如果用户的代码包含该名称的变量,它将自动重命名而不是相互冲突。任何局部变量都应该以这种方式保留。
第三步:执行js代码
try {
eval(code);
} catch (e) {
alert(e);
}
注意:eval 是js提供的一个强大的方法。在纯前端应用中,我们很少会用到它。但在这里,eval函数必不可少:它可以把一个字符串解析成js语句并执行!
2 高亮块(仅限Web)
在blockly web应用程序中,我们通常会需要一种逐句执行的效果:在执行某句代码时,把它对应的block高亮显示,起到分步提醒的作用。这是一种非常好的用户体验。
这个效果可以通过在生成JavaScript代码之前,逐个语句级别设置STATEMENT_PREFIX
来实现:
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
Blockly.JavaScript.addReservedWords('highlightBlock');
定义highlightBlock
在工作区上标记块。
function highlightBlock(id) {
workspace.highlightBlock(id);
}
这会导致在highlightBlock('123');
每个语句前添加语句,其中123
是要突出显示的块的序列号。
3. 无限循环的特殊处理
尽管我们生成的代码在语法上是正确的,但它可能包含无限循环。包含无限循环的代码会超出了Blockly的处理范围。处理这些情况的最佳方法是维护一个计数器并在每次迭代执行时减少计数器。要做到这一点,只需设置Blockly.JavaScript.INFINITE_LOOP_TRAP
一个代码片段,将其插入每个循环和每个函数中。这里是一个例子:
window.LoopTrap = 1000;
Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);
image.png
image.png
例
JS解释器
如果你认真地正确地运行用户块,那么 JS解释器项目就是要走的路。此项目与Blockly分开,但是专门为Blockly编写。
- 以任何速度执行代码。
- 暂停/恢复/逐步执行。
- 在执行时突出显示块。
- 完全与浏览器的JS隔离。
这是一个 使用Blockly和JS解释器来生成和执行JavaScript 的现场演示。
运行解释器
首先,从GitHub下载JS Interpreter:github
image.png然后将其添加页面中:
<script src="acorn_interpreter.js"></script>
调用它的最简单方法是:生成JavaScript,创建解释器,并运行代码:
var code = Blockly.JavaScript.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
也就是用上面的三行代码来代替下面的两行代码去运行js。
var code = Blockly.JavaScript.workspaceToCode(demoWorkspace);
eval(code)
如果你在运行的过程中遇到的问题,不要急,先向下看。
步骤解释器
为了更慢地执行代码或以更受控制的方式执行代码,请将调用替换run
为循环(在此情况下每10ms执行一步):
function nextStep() {
if (myInterpreter.step()) {
window.setTimeout(nextStep, 10);
}
}
nextStep();
请注意,每一步不是一行或一个块,它是JavaScript中的一个语义单位,它可能非常精细。
浏览器隔离的沙盒
JS解释器是一个完全与浏览器隔离的沙盒。任何执行外部操作的块都需要将API添加到解释器中。有关完整说明,请参阅 JS解释器文档。
上面的这段说明很重要:通过如下的
var code = Blockly.JavaScript.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
的方式运行js代码有一个前提:即code中的函数调用,方法调用都必须提前在InterPreter这个沙盒中注册过。
如果在code中有代码"alert(1)",如果你用eval(code)去运行,则是可以的,因为当前是在window环境中运行,而用上面的方式去运行就会出错:原因是alert并没有在interpreter环境中提前去注入!
这样做的好处是:用Interpreter统一管理blockly的代码。形成一个封装的环境,从而避免了代码污染。缺点要先注册你的函数,这个过程是比较繁琐的。
注入api的示例
我们把alert 和 prompt注入到interpreter环境中。
第一步:定义注入工具函数
function initApi(interpreter, scope) {
// Add an API function for the alert() block.
var wrapper = function(text) {
return alert(arguments.length ? text : '');
};
interpreter.setProperty(scope, 'alert',
interpreter.createNativeFunction(wrapper));
// Add an API function for the prompt() block.
wrapper = function(text) {
return prompt(text);
};
interpreter.setProperty(scope, 'prompt',
interpreter.createNativeFunction(wrapper));
}
第二步:在解释器初始化时传递initApi函数:
var myInterpreter = new Interpreter(code, initApi);
经过这两步之后,如果你的代码中有alert或者prompt 函数,它们也会正常运行了。
示例2 highlightBlock()
highlighBlock是blockly提供的,如果你希望它在JS Interpreter中也能正常运行,你必须创建一个包装函数highlightBlock()来捕获函数参数,并将其注册为本机函数。
function initApi(interpreter, scope) {
// Add an API function for highlighting blocks.
var wrapper = function(id) {
return workspace.highlightBlock(id);
};
interpreter.setProperty(scope, 'highlightBlock',
interpreter.createNativeFunction(wrapper));
}
网友评论