Blockly 可视化编程编辑器入门教程:搭积木生成代码,10 分钟从 0 到跑通
Blockly 是一个基于 Web 的可视化编程编辑器,它把变量、循环、判断这些编程概念做成能拖拽拼接的积木,再把拼好的积木直接翻译成 JavaScript、Python、PHP、Lua、Dart 等语言的可运行代码。想给学生做编程练习、给产品加一个"零门槛写逻辑"的界面,用它最省事。
先跑起来:一个能拖积木的编辑器
这节只干一件事:让你尽快看到"拖积木"的效果。先准备一个 index.html,内容就是下面这些:
<!DOCTYPE html>
<html>
<body style="margin:0">
<div id="blocklyDiv" style="width:800px;height:500px"></div>
<xml id="toolbox" style="display:none">
<block type="text_print"></block>
<block type="controls_repeat_ext">
<value name="TIMES"><shadow type="math_number"><field name="NUM">3</field></shadow></value>
</block>
<block type="logic_compare"></block>
</xml>
<script type="module">
import * as Blockly from 'blockly';
// 把编辑器注入到指定 div,工具栏来自隐藏的 toolbox
const workspace = Blockly.inject('blocklyDiv', {
toolbox: document.getElementById('toolbox'),
});
</script>
</body>
</html>
浏览器打开后,#blocklyDiv 里就是一个可以缩放、滚动、拖拽的积木工作区。Blockly.inject() 是唯一入口:传一个容器,再传工具栏,其余全由库接管。
有两点要交代清楚:
- 如果你的项目用 webpack、Vite 等打包工具,
import * as Blockly from 'blockly'直接生效; - 如果想在浏览器里直接跑上面的 demo,需要先装包,把
dist/里的编译产物引进页面(见下面源码方式),效果一样。
从源码编译出 dist 产物
git clone https://gitcode.com/gh_mirrors/bloc/blockly
cd blockly
npm install
npm run build # 产物落在 dist/ 下
装好后在 index.html 里按顺序引入三个文件,页面即可运行:
<script src="dist/blockly_compressed.js"></script>
<script src="dist/blocks_compressed.js"></script>
<script src="dist/javascript_compressed.js"></script>
blockly_compressed.js 是编辑器内核,blocks_compressed.js 是内置积木,javascript_compressed.js 是 JavaScript 代码生成器——三者分工在后面讲。仓库自带的完整示例在 demos/code/index.html,它额外把五种语言的生成结果并排展示,想抄一个现成的就抄它。
项目拆解:仓库由哪几部分组成
这节帮你建立一张仓库地图,后面读源码、查文档都能直接定位。
- core/:编辑器主体。
core/inject.ts处理注入逻辑,core/workspace.ts管理工作区(积木的集合、坐标、选择),core/block.ts与core/block_svg.ts分别定义积木本身和它的屏幕渲染。 - blocks/:内置积木的 TypeScript 定义,按功能分文件:
logic.ts(逻辑)、loops.ts(循环)、math.ts(数学)、lists.ts(列表)、procedures.ts(函数)等。 - generators/:代码生成器。每种语言一个文件夹加一个入口文件,入口把该语言下所有积木的生成函数组装成一个实例,比如
generators/javascript.ts导出javascriptGenerator。 - demos/:可运行的成品示例——
demos/code/(代码生成)、demos/blockfactory/(积木工厂)、demos/minimap/(小地图)、demos/storage/(云存储)。 - tests/mocha/:单元测试,是查 API 用法最快的地方,每个核心模块都有对应测试。
- msg/:100 多种语言的文本包(工具箱分类名、积木提示文案),改界面语言看这里。
场景实战
把积木编译成能跑的代码
最高频的需求:用户拖完积木,你要拿到真实代码。生成器干的事很直白——遍历工作区里的每块积木,调用它对应的生成函数,再拼成完整程序:
import {javascriptGenerator} from 'blockly/javascript';
const code = javascriptGenerator.workspaceToCode(workspace);
console.log(code);
想"边拖边刷新",就监听积木变化事件(Blockly.Events.BLOCK_CHANGE 对应所有会改变工作区的操作):
workspace.addChangeListener((event) => {
if (event.type === Blockly.Events.BLOCK_CHANGE) {
console.log(javascriptGenerator.workspaceToCode(workspace));
}
});
效果:积木区里每搭一步,控制台就同步输出对应的 JavaScript。事件类型全表在 core/events/,想监听"拖拽""选中"等更细的操作,那里都有。
三步注册一块自定义积木
这节讲"我的业务需要一块官方没有的积木"怎么做。分三步:定义积木长相 → 写生成函数 → 验证。
第一步,注册积木定义,声明它的文案、参数和连接方式:
Blockly.defineBlocksWithJsonArray([
{
type: 'say_hello',
message0: '向 %1 问好',
args0: [{type: 'field_input', name: 'WHO', text: '世界'}],
previousStatement: null, // 允许上下拼接
nextStatement: null,
colour: 160,
},
]);
第二步,给它写生成函数,告诉生成器"这块积木翻译成什么代码":
javascriptGenerator.forBlock['say_hello'] = function(block) {
const who = javascriptGenerator.valueToCode(block, 'WHO') || "''";
return `console.log('Hello, ${who}');\n`;
};
第三步,把 say_hello 写进工具箱 XML,拖进工作区、运行生成,看到输出即成功。
一个提醒:积木定义和生成函数是两回事,只注册积木不写生成函数,拖是能拖,生成代码时会提示缺失;反之亦然。官方积木的写法可以直接抄 blocks/logic.ts,它展示了语句块、值块、下拉菜单的完整参数。
保存和恢复整个工作区
用户关掉页面再打开,积木还在——这靠序列化。推荐用 JSON(比 XML 更适合程序处理):
// 存成 JSON 对象,塞进 localStorage 或发给你的后端
const data = Blockly.serialization.workspaces.save(workspace);
localStorage.setItem('blocks', JSON.stringify(data));
// 下次打开时读回来
const saved = JSON.parse(localStorage.getItem('blocks'));
Blockly.serialization.workspaces.load(saved, workspace);
序列化会连同变量、自定义函数、工作区注释一起存,恢复后布局不变。仓库里的 demos/storage/ 是完整实现:它把保存/加载接到了一个带用户体系的云存储后端上,想做"多用户各自保存"可以直接参考它的 appengine/ 后端代码。
避坑指南
现象:工具箱里有分类,点进去却是空的。 原因:积木定义没注册成功,或工具箱 XML 里 type 写错了——两者必须一字不差。工具箱只是"目录",真正能被拖出来的是已注册的积木。 对策:看浏览器控制台的报错;先塞一个 text_print 这类内置积木验证链路,再逐个排查自定义类型名。
现象:workspaceToCode() 返回空字符串,或提示某积木没有生成函数。 原因:积木库和生成器是两个独立的产物,blocks_compressed.js 只负责"能拖",代码要靠 javascript_compressed.js(或 npm 里的 blockly/javascript)翻译。只引了前者,生成自然是空的。 对策:检查引入是否成对出现;自定义积木则确认每种目标语言都配了 forBlock 函数。
现象:注入成功,但编辑器高度是 0 或显示错位。 原因:Blockly.inject() 依赖容器自身的宽高来渲染,#blocklyDiv 没设明确高度就会塌掉。 对策:给容器写死宽高,或设 height: 100% 并确保父容器有高度;窗口缩放后调用 workspace.resize() 重新测量。
延伸路径
从"能拖积木"到"能出代码",你只需要记住两件事:Blockly.inject() 负责把编辑器摆上屏,generator.workspaceToCode() 负责把积木变成真代码,中间的一切——自定义积木、主题、事件、保存恢复——都是围绕这两端的扩展。
继续深入看这三处:tests/playground.html 是仓库自带的调试台,可以现场改积木、看 XML/JSON;tests/mocha/ 里每个核心模块都有测试,是查 API 标准用法的捷径;想换配色和字体,从 core/theme/ 下手,内置主题的完整写法见 core/theme/classic.ts。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



