MakeCode扩展开发指南:从自定义积木到开源硬件编程工具 1. 从使用者到创造者为什么你需要掌握MakeCode扩展开发如果你和我一样是从玩micro:bit或者Adafruit Circuit Playground Express这类教育硬件入门的那你对MakeCode的积木块编程界面一定不陌生。拖拖拽拽就能让LED闪烁、让蜂鸣器唱歌这种低门槛的体验是吸引无数新手和教育工作者的关键。但玩久了你可能会遇到一个瓶颈官方提供的积木块功能是有限的。当你想要驱动一个特定的传感器或者实现一个复杂的算法逻辑时常常会发现工具箱里没有现成的积木。这时候大多数人会选择在JavaScript视图里写代码但这就失去了积木编程直观、易分享的优势。MakeCode扩展开发正是为了解决这个痛点而生的。它本质上是一套“元编程”工具让你能把自己写的JavaScript/TypeScript函数“包装”成新的、可拖拽的积木块然后分享给全世界的用户。这不仅仅是“自定义积木”更是一种构建可复用代码库、甚至是为特定硬件比如你手头那块小众但好用的传感器板创建专属编程环境的能力。从技术角度看MakeCode扩展是一个标准的GitHub代码仓库里面包含了你的源代码、一个定义依赖和元数据的pxt.json文件以及通过特殊注释//%来告诉编辑器如何生成积木块的“配方”。这种设计巧妙地将开源协作GitHub、现代前端工程TypeScript和可视化编程Blockly结合在了一起。所以这篇指南适合谁首先当然是那些已经用MakeCode做过项目手里攒了一些好用但零散的代码想把它变成标准工具的人。其次是硬件开发者或老师你可能为某个课程或产品设计了一套硬件需要配套的、适合学生使用的编程积木。最后哪怕你只是对“工具是如何被制造出来的”感到好奇这个过程也能让你对现代编程工具的底层逻辑有更深刻的理解。整个过程不需要你是Git专家也不需要精通复杂的构建工具MakeCode的Web编辑器已经帮你处理了大部分繁琐的步骤。2. 核心原理与设计思路GitHub仓库如何变成可拖拽的积木在深入动手之前我们有必要拆解一下MakeCode扩展背后的运行机制。理解了这个你就能明白为什么每一步要那样做遇到问题时也知道该从哪里排查。2.1 扩展的本质一个特化的GitHub仓库MakeCode没有自己搞一套复杂的扩展商店或包管理系统而是直接利用了GitHub。你的每一个扩展本质上就是一个公开的GitHub代码仓库。这个设计非常聪明它直接继承了GitHub的版本管理、协作审查和开源分发能力。当你在MakeCode编辑器中搜索或添加一个扩展时编辑器实际上是在向GitHub的API发送请求获取特定仓库的信息和代码。仓库里最关键的文件是pxt.json你可以把它理解为这个扩展的“身份证”和“说明书”。它定义了扩展的名称、描述、版本、依赖哪些其他扩展、支持哪些硬件平台target如microbit、adafruit等以及源代码文件的位置。当MakeCode加载你的扩展时第一件事就是读取这个文件。2.2 从代码到积木注释即配置Annotation as Configuration这是MakeCode扩展开发中最有特色的部分。它没有采用独立的、复杂的配置文件如XML或JSON来定义积木块的外观和行为而是使用了JSDoc注释结合特殊标记的方式。具体来说你在TypeScript函数上方写的注释除了用于文档还可以包含以//%开头的指令。例如/** * 让LED闪烁一次。 */ //% block闪烁LED weight100 color#FF6B6B export function blinkLed(): void { // ... 实现代码 }这里的//% block就告诉MakeCode“请为这个函数生成一个积木块”。block后面双引号里的文字会成为积木上显示的文字。weight控制这个积木在工具箱类别中的排序数字越大越靠前color则定义了积木的颜色。这种“配置即注释”的方式让代码和积木定义紧密耦合维护起来非常方便——你修改函数签名比如增加一个参数对应的积木块形状会自动更新。2.3 静态TypeScript安全与性能的保障你可能注意到MakeCode扩展用的是“Static TypeScript”而不是普通的JavaScript。这是微软为教育场景和嵌入式环境定制的一个TypeScript子集。它最大的特点是“静态”在代码被下载到硬件如micro:bit上运行之前所有的类型检查、语法转换翻译成C或汇编都已经在网页编辑器里完成了。这意味着安全性避免了动态类型和eval等危险操作适合学生在课堂上随意尝试。性能生成的机器码更高效对资源紧张的微控制器至关重要。工具支持能提供更好的代码补全和错误提示。对于扩展开发者来说你几乎可以把它当作标准的TypeScript来写只是要避免使用一些浏览器或Node.js特有的API比如document对象或fs模块。你的代码最终会被编译成目标硬件所能执行的格式。2.4 同步与发布流程一个简化的Git工作流MakeCode编辑器内置了一个极度简化的Git客户端。你不需要知道git add,git commit,git push的具体命令。点击那个“GitHub同步”按钮编辑器会帮你完成以下工作检查本地文件的更改。让你输入提交信息。可选让你决定是否“Bump”版本号。自动执行pull拉取远程可能存在的更新、合并冲突或创建PR、push推送你的更改。这个设计大幅降低了使用门槛但也隐藏了一些细节。理解其背后的Git操作对于处理多人协作冲突或进行高级管理至关重要。3. 从零开始创建你的第一个扩展手把手实操指南理论说得再多不如动手做一遍。让我们以一个实际场景为例为Adafruit Circuit Playground Express创建一个扩展添加一个能计算三个数字平均值的积木块。3.1 前期准备关联GitHub账号创建或登录GitHub账号如果你还没有去 github.com 注册一个免费账户。这是必须的。获取GitHub个人访问令牌Token在MakeCode编辑器中以Adafruit版为例随便打开或新建一个项目。点击右上角的齿轮图标设置选择“扩展Extensions”。在扩展对话框的底部你会看到一个链接“登录GitHub以关联你的扩展”或类似文字。点击它。这会弹出一个向导引导你到GitHub网站生成一个Token。这个Token相当于一把“专用钥匙”允许MakeCode编辑器代表你访问和操作GitHub仓库仅限于扩展相关操作。请务必为这个Token选择repo和workflow权限。生成后将Token字符串复制回MakeCode弹出的对话框中保存。注意Token只显示一次请妥善保管。如果丢失需要到GitHub的Settings - Developer settings - Personal access tokens中撤销旧Token并生成新的。3.2 创建并初始化扩展仓库关联账号后创建扩展仓库变得异常简单。回到MakeCode编辑器主页。点击紫色的“导入Import”按钮。你会发现多了一个选项“从你的GitHub仓库导入”或“新建GitHub仓库”。选择“新建GitHub仓库”。输入仓库名称。遵循pxt-前缀的约定是个好习惯例如pxt-cpx-math-tools这能让其他人一眼看出这是MakeCode扩展。点击创建。MakeCode会自动为你初始化这个仓库并打开一个新的编辑器窗口。初始化文件解析 此时你的项目资源管理器左侧边栏里应该已经生成了几个核心文件main.ts你的主要TypeScript源代码文件。积木块的定义代码就写在这里。pxt.json扩展的配置文件。打开看看里面已经填好了名称、描述、依赖等基础信息。README.md扩展的说明文档。好的README对用户非常重要。test.ts用于本地测试的文件。当你直接运行这个扩展项目而非作为扩展导入其他项目时这里的代码会执行。_locales/如果需要多语言支持可以把不同语言的积木名称翻译文件放在这里。3.3 编写核心代码与定义积木块现在我们来编写计算平均值的函数并把它变成积木。打开main.ts清空原有内容写入以下代码/** * 提供一些有用的数学工具函数。 */ //% color#AA278D icon\uf1ec weight90 namespace mathTools { /** * 计算三个数字的平均值。 * param a 第一个数字 * param b 第二个数字 * param c 第三个数字 */ //% block计算平均值 |a %a|b %b|c %c //% blockIdmath_avg_three //% weight100 export function averageThree(a: number, b: number, c: number): number { return (a b c) / 3; } /** * 将摄氏温度转换为华氏温度。 * param celsius 摄氏温度值 */ //% block摄氏转华氏 |celsius %celsius //% blockIdmath_celsius_to_fahrenheit //% weight90 export function celsiusToFahrenheit(celsius: number): number { return celsius * 1.8 32; } }代码逐行解读namespace mathTools创建了一个命名空间mathTools它将成为积木工具箱中的一个新类别抽屉。//% color#AA278D icon\uf1ec weight90这是命名空间级别的注释指令。它定义了整个mathTools类别的颜色、图标这里是FontAwesome的计算器图标和在工具箱中的权重排序。//% block计算平均值 |a %a|b %b|c %c这是函数级别的注释指令用于定义积木。block后面的字符串定义了积木上显示的文字。竖线|用于分隔积木的不同部分%a表示这里是一个名为a的输入插槽。blockIdmath_avg_three为这个积木指定一个唯一的ID。强烈建议手动设置一个清晰的ID这有助于MakeCode内部管理和未来可能的积木升级兼容。如果不指定MakeCode会自动生成一个但可能不易读。weight100这个积木在mathTools类别内部的排序权重。export function averageThree(...): number一个普通的TypeScript函数export关键字使其可以被外部访问。它的参数和返回值类型决定了积木的输入输出形状。3.4 “天才技巧”实时测试你的积木这是开发过程中最高效的一步很多官方文档只是一笔带过但却是提升开发体验的关键。打开两个浏览器标签页标签页A保持打开你正在编辑的扩展项目即pxt-cpx-math-tools这个项目。标签页B访问MakeCode编辑器主页点击**“新建项目”**创建一个普通的测试项目。在**标签页B测试项目**中点击右上角齿轮图标 - 选择“扩展”。在扩展搜索/列表中你应该能看到你的扩展例如mathTools或你的仓库名。点击添加它。添加成功后在测试项目的积木工具箱中你应该能看到一个新的MATH TOOLS类别里面有你刚定义的“计算平均值”和“摄氏转华氏”积木。现在开始实时同步在**标签页A扩展项目**中修改你的代码比如改变积木文字block后的内容或者增加一个函数。点击编辑器上方的**“GitHub同步”按钮**一个带GitHub标志的循环箭头提交你的更改。在提交时先不要勾选“Bump version”我们还在开发测试阶段。提交完成后切换到标签页B测试项目。大多数情况下编辑器会自动检测到扩展有更新并提示你重新加载。如果没有手动刷新一下页面。刷新后你就能立即在测试项目中看到代码修改后的效果这个“双标签页”工作流实现了接近本地开发的“修改-保存-刷新”的快速迭代体验对于调整积木外观和测试功能至关重要。4. 深入自定义积木参数、颜色与高级形状掌握了基础积木创建后我们可以让积木变得更强大、更美观。MakeCode提供了丰富的注释指令来控制积木的方方面面。4.1 丰富积木的参数类型积木的输入槽input类型由函数参数的类型决定但你可以通过注释进行更精细的控制。namespace advancedBlocks { /** * 从列表中选择一个随机项目。 * param list 输入的列表 */ //% block从列表 %list 中随机选择 //% blockIdadvanced_pick_random export function pickRandomT(list: T[]): T { if (list.length 0) return null; const index Math.randomRange(0, list.length - 1); return list[index]; } /** * 等待直到按钮被按下。 * param button 需要等待的按钮 */ //% block等待按钮 %button 被按下 //% blockIdadvanced_wait_for_button //% button.shadowdeviceButton export function waitForButton(button: number): void { // 假设button是一个数字代表的按钮ID // 实际实现会调用具体的硬件API // 此处仅为示例 } /** * 设置NeoPixel灯带的颜色。 * param strip NeoPixel灯带对象 * param color 颜色值 */ //% block设置灯带 %strip 颜色为 %color //% blockIdneopixel_set_color //% strip.shadowvariables_get strip.deflstrip //% color.shadowcolorNumberPicker export function setStripColor(strip: neopixel.Strip, color: number): void { strip.showColor(color); } }关键注释指令解析//% button.shadowdeviceButtonshadow属性用于指定输入槽的“影子块”。deviceButton是一个预定义的影子块它会显示为一个下拉菜单让用户选择“A按钮”、“B按钮”等。这比让用户直接输入数字友好得多。//% strip.shadowvariables_get strip.deflstripvariables_get影子块会让输入槽显示为一个变量下拉列表。deflstrip设置了默认的变量名为strip方便用户使用。//% color.shadowcolorNumberPickercolorNumberPicker会渲染一个颜色选择器用户可以直接点击选择颜色而不是输入颜色代码。4.2 控制积木的外观与分组//% block向左转 %angle 度 blockGap8 //% angle.min0 angle.max360 angle.defl90 //% group移动控制 subcategory转向blockGap8设置这个积木与上下其他积木之间的间隔像素让工具箱看起来更宽松。angle.min0 angle.max360为数字输入参数angle设置最小值和最大值编辑器会进行输入限制。angle.defl90设置参数angle的默认值为90。group移动控制在命名空间内进一步分组。如果namespace是顶层类别group就是里面的子类别。subcategory转向在group下的进一步细分。这用于功能非常丰富的扩展保持工具箱的条理性。4.3 实现“语句”、“值”和“处理器”积木函数返回值决定了积木是“值积木”有输出可以嵌入其他积木还是“语句积木”独立执行无输出。// 语句积木没有返回值 (void) //% block播放音效 %sound export function playSound(sound: number): void { ... } // 值积木有返回值 (非void) //% block读取温度 export function readTemperature(): number { ... } // 处理器积木事件使用 //% block当 %event 发生时 和 handler 参数 //% block当设备晃动时 //% blockIdon_shake export function onShake(handler: () void): void { // 这里会调用底层的硬件事件注册API // handler是用户拖入的事件处理函数 }对于事件处理器积木用户可以将其他积木块拖入其“身体”内部这些内部的积木就是handler函数的内容。5. 版本管理、冲突处理与发布上线当你的扩展功能完善准备分享给他人使用时就需要理解MakeCode的版本管理和发布流程。5.1 Bump Version标记稳定版本这是MakeCode扩展发布中最重要也最容易忽略的概念。什么是Bump在点击“GitHub同步”按钮时弹出的对话框中有一个复选框“Bump version (major.minor.patch)”。勾选它就意味着你正式发布当前提交的代码为一个新的、稳定的版本。Bump的作用用户可见只有被“Bumped”的版本才会出现在公开的扩展搜索和添加列表中。未Bump的提交只存在于Git历史中普通用户无法直接获取。升级提示当用户的项目使用了你扩展的旧版本而你已经发布了新Bump的版本时用户的编辑器里会在扩展旁边显示一个“升级”小按钮。版本锁定当用户通过扩展对话框添加你的扩展时他们添加的是最新Bumped的版本。这保证了用户获得的是一个经过你确认的稳定版本而不是你正在开发的、可能包含错误的中间状态。实操建议在开发调试阶段频繁提交但不要Bump。当你完成了一个完整的功能并经过充分测试后进行一次提交并勾选Bump。遵循 语义化版本规范 是个好习惯PATCH(0.0.X)向后兼容的问题修复。MINOR(0.X.0)向后兼容的新功能。MAJOR(X.0.0)不兼容的API修改。5.2 处理Git冲突多人协作的必修课如果你和他人共同维护一个扩展或者你在不同电脑上编辑可能会遇到同步冲突。MakeCode编辑器的冲突处理流程当你尝试推送Sync时如果GitHub上的远程仓库已经有了你本地没有的新提交别人先推送了编辑器会尝试自动合并。如果自动合并成功比如修改了不同文件你会直接推送成功合并了双方的更改。如果自动合并失败比如你们修改了同一文件的同一行代码MakeCode会执行以下操作在GitHub上创建一个新的分支例如conflict-branch-xxx。将你的提交推送到这个新分支。在GitHub上创建一个Pull Request (PR)将你的分支合并到主分支main或master。在MakeCode编辑器中弹出一个对话框提示冲突发生并提供一个链接让你跳转到GitHub网站去手动解决冲突。手动解决冲突步骤点击对话框中的链接在GitHub的PR页面你会看到冲突的文件和具体冲突行被标记包围的部分。你需要决定保留哪一部分的代码或者进行修改以融合两者。在GitHub网页编辑器里直接编辑文件解决这些冲突标记。解决完毕后在PR页面点击“Mark as resolved”然后“Commit merge”。回到MakeCode编辑器关闭冲突提示对话框并再次点击同步按钮。此时编辑器会拉取已解决冲突的最新主分支代码你的本地工作区就更新了。重要心得养成在开始编辑前先“同步”一下的好习惯可以最大程度减少冲突。如果冲突不可避免不要慌张GitHub的PR界面提供了非常直观的解决冲突工具。5.3 提交审核进入官方扩展库默认情况下你的扩展只能通过“输入GitHub仓库URL”的方式添加。如果你希望你的扩展出现在MakeCode编辑器的扩展搜索对话框里让所有用户都能轻松搜到你需要提交审核。确保扩展质量代码完整、有清晰的README.md、积木分类合理、经过测试。提交审核请求前往你的扩展所对应硬件平台的官方pxt仓库的Issues页面。例如Adafruit平台的扩展是提交到Microsoft/pxt-adafruit根据原文。注意不同硬件平台Target的审核仓库不同。micro:bit是Microsoft/pxt-microbitArcade是Microsoft/pxt-arcade。请务必确认你的扩展是为哪个平台开发的并提交到对应的仓库。新建一个Issue标题类似“Request for extension approval: pxt-my-awesome-extension”。在内容中提供你的扩展GitHub仓库链接并简要说明扩展的功能和用途。等待审核维护者会检查你的代码安全性、功能性和文档。通过后你的扩展就会被加入官方索引通常几小时或一两天后用户就能在扩展对话框中搜索到了。6. 进阶技巧与避坑指南实录基于我实际开发扩展的经验这里有一些官方文档可能没强调但能让你事半功倍或避免头疼的技巧。6.1 利用test.ts进行快速单元测试test.ts文件是一个强大的本地测试工具。它的代码不会被打包进最终发给用户的扩展里只在你直接运行这个扩展项目时执行。// 在 test.ts 中 namespace tests { // 这是一个简单的测试函数 function testAverage() { const result mathTools.averageThree(10, 20, 30); console.log(Average test: ${result}); // 会在调试控制台输出 // 简单的断言虽然MakeCode没有内置断言库但可以自己判断 if (result ! 20) { console.error(Test failed! Expected 20, got result); } else { console.log(Test passed!); } } // 注册一个事件在程序启动时运行测试 control.inBackground(() { pause(1000); // 等待系统稳定 console.log(Running extension tests...); testAverage(); // ... 调用其他测试函数 console.log(Tests finished.); }); }用法在扩展项目里你可以像写普通程序一样在test.ts里调用你main.ts里定义的函数用console.log输出结果到浏览器的开发者工具控制台F12打开。这是验证函数逻辑是否正确的最快方法无需切换到另一个测试项目。6.2 调试与问题排查清单当你创建的积木不显示、行为异常时可以按以下顺序排查问题现象可能原因排查步骤积木在工具箱中完全不显示1. 代码语法错误2.pxt.json配置错误3. 未成功添加扩展到测试项目1. 检查编辑器下方是否有红色错误提示。2. 检查pxt.json的name,description,dependencies字段格式是否正确。3. 在测试项目中确认已从扩展列表成功添加。尝试移除后重新添加。积木显示但类别/颜色不对命名空间或积木的注释指令错误1. 检查namespace上方的//% color和icon指令。2. 检查//% block指令的拼写和格式特别是引号和%号。积木参数下拉菜单不显示选项shadow指令使用错误或对应的影子块不存在1. 确认使用的shadow类型是MakeCode支持的如deviceButton,colorNumberPicker。2. 对于自定义枚举需要使用//% block选项 %param和param.fieldEditorgridpicker等更复杂的配置。扩展同步后测试项目无变化1. 测试项目未刷新2. 扩展提交后未Bump版本3. 浏览器缓存1. 手动刷新测试项目页面。2. 确认在扩展项目中提交时是否勾选了Bump如果没有普通提交不会触发用户端更新。3. 尝试清除浏览器缓存或使用隐私模式。GitHub同步失败报权限错误GitHub Token失效或权限不足1. 去GitHub的Token设置页面检查Token是否被撤销。2. 确认Token拥有repo和workflow权限。3. 在MakeCode设置中尝试重新登录GitHub。6.3 性能与内存注意事项为资源受限的微控制器如micro:bit编写扩展时需特别注意避免全局变量和大型数组尽量使用局部变量和函数参数。静态分配的大数组会永久占用宝贵的内存。谨慎使用pause和循环在事件处理器或后台函数中长时间的pause或紧密循环会阻塞整个程序导致设备无响应。考虑使用control.inBackground或将任务拆分。及时清理资源如果你创建了硬件对象如I2C设备、NeoPixel灯带在不需要时确保有方法可以关闭或释放它们。虽然很多微控制器会断电重置但良好的编程习惯很重要。浮点数运算在早期的micro:bit v1等不支持硬件浮点的平台上浮点运算非常慢且耗内存。尽量使用整数运算例如用123代表12.3℃使用时除以10。6.4 扩展的依赖管理你的扩展可能需要依赖其他扩展比如你要用neopixel库来控制灯带。这在pxt.json文件中管理{ name: pxt-my-complex-extension, dependencies: { core: *, neopixel: github:microsoft/pxt-neopixel#v0.7.3 }, description: ..., files: [ main.ts ], targetVersions: { target: adafruit, version: 1.8.22 } }core: *表示依赖目标平台的核心库通常是必须的。neopixel: github:microsoft/pxt-neopixel#v0.7.3这是一个具体的依赖声明。它告诉MakeCode“我的扩展需要neopixel库请从指定的GitHub仓库获取并使用v0.7.3这个标签版本。”最佳实践尽量指定具体的版本号如#v0.7.3而不是*或latest。这能确保你的扩展在不同时间、不同用户的环境下行为一致避免因为依赖库的更新导致你的扩展突然出错。开发MakeCode扩展是一个连接创意与实现、个人开发与社区共享的绝佳过程。从把一段有用的代码封装成一个积木开始到最终它被成千上万的学生或开发者使用这种成就感远超仅仅完成一个私人项目。最关键的是迈出第一步创建你的仓库写出第一个//% block注释然后利用“双标签页”法快速迭代。遇到问题多查官方文档多在社区如MakeCode论坛提问。随着你创建的积木越来越多你会逐渐形成自己的代码风格和最佳实践最终成为这个可视化编程生态的贡献者而不仅仅是使用者。