修改分块定义

一个常见问题是如何修改现有代码块的定义。例如,您可能需要添加连接检查或将字段更改为值输入。

通常,无法原地修改块定义。

如何修改现有定义

如果您想修改现有分块类型的定义,请执行以下操作:

  1. 复制现有定义(包括块代码生成器)。
  2. 修改副本,并为您的类型命名。
  3. 将新定义添加到 Blockly.Blocks

为什么我无法直接修改现有定义?

如果您想了解为何无法修改现有定义,请继续阅读。我们会考虑一些可能的原因。

直接修改现有定义

您可以通过以下两种方式直接修改现有代码块的定义:猴子补丁和代码分支。我们强烈建议您不要这样做,因为您可能会破坏依赖于猴子补丁或分叉代码的代码。这两种方法还会导致难以集成更新和 bug 修复。如需了解详情,请参阅猴子补丁分叉 Blockly

对现有定义进行子类化

您可能会想到以某种方式对现有定义进行子类化。很遗憾,这是不可能的,因为块定义不是类,而是混入。例如,由于定义中没有颜色属性,因此无法覆盖颜色属性。而是有一个 init 函数,该函数会调用 setColour 来设置块的颜色属性。由于调用位于 init 内,因此无法在不替换整个 init 函数的情况下替换它。

覆盖现有定义中的函数

您可以覆盖现有定义中的函数:

Blockly.Blocks['existing_block'].init = function() {/*new function*/};

这种方法可行,但您必须复制并修改现有函数,而不能神奇地替换一行代码。这会导致以下几个问题:

  • 这与复制并修改整个定义没有太大区别。
  • 您无法使用它修改在 JSON 中定义的块的 init 函数,例如 Blockly 的内置块。这是因为没有要复制的 init 函数,该函数是在运行时生成的。
  • 它要求您使用 JavaScript 定义代码块,这可能会导致本地化问题

覆盖 init 的结果

如需“仅替换 init 函数的一行代码”,一种方法是将 init 函数替换为调用原始 init 函数并覆盖该调用结果的函数。例如,以下代码会更改 logic_null 代码块的颜色:

const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
  originalInit.call(this);
  this.setColour(300);
}

遗憾的是,这并不像看起来那么有用。例如:

  • 扩大连接检查范围或应用限制较少的字段验证器可能会使块代码生成器和事件处理脚本做出的假设失效。

  • 将字段替换为值输入会破坏块代码生成器和字段验证器,并且可能会破坏事件处理脚本。对于本地化版块,这可能也非常困难,因为不同的语言区域可能会导致这些块具有不同的输入和字段类型和顺序

覆盖 JSON 定义中的键值对

如果某个分块的 JSON 是公开的,则可能可以覆盖单个 JSON 值。例如:

// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
  init: function() {
    initJson(blockJson); // Called when the block is created.
  }
}

// Third-party code.
blockJson.colour = 100;

不过,只有在 JSON 对象可供公开访问、定义明确定义了 init 函数且 init 函数调用了 initJson 的情况下,这种方法才有效。如果将 JSON 传递给 defineBlocksWithJsonArraycreateBlockDefinitionsFromJsonArray,则该方法不起作用,因为 JSON 会在第三方有机会修改它之前进行处理。(请注意,Blockly 的内置块使用 createBlockDefinitionsFromJsonArray。)

但如果 JSON 不是以这种方式定义的,该怎么办?应该仍然可以覆盖 JSON 属性吗?很抱歉,不能。该定义包含 init 函数(而非 JSON),并且没有用于将 init 函数转换为 JSON 的函数。

// Doesn't work. There is no getJson() function.
const json = Blockly.Blocks['existing_block'].getJson();
json['message0'] = 'my new message0';
Blockly.Blocks['existing_block'].init = function () {
  initJson(json);
};

从设计上考虑可重复使用

在设计自定义块时,您或许可以采用有助于提高重用性的设计方式。

重复使用 JSON

如果您有两个基本相似的块,可以创建一个父级 JSON 定义,并在子级定义中重复使用该定义。例如:

const parentJson = {
  // shared properties
};

Blockly.Blocks['child_block_1'] = {
  init: function() {
    initJson({...parentJson, colour: 100})
  }
}

Blockly.Blocks['child_block_2'] = {
  init: function() {
    initJson({...parentJson, colour: 200})
  }
}

另一种方法是在公开可用的对象中定义 JSON,并将该对象传递给 init 函数中的 initJson。这样一来,其他人就可以覆盖各个媒体资源。如需了解详情,请参阅覆盖 JSON 定义中的键值对

重复使用函数

块可以定义多个标准函数,例如块级事件处理脚本、自定义提示、字段验证器和更改器使用的函数,以及提供自定义行为的函数,例如用于根据外部数据(例如机器人手臂的当前位置)设置字段值的函数。

这些函数或许可以在多个代码块中重复使用。

使用下拉菜单字段

如果您有一组除了运算符之外基本相同的块,则可以设计一个包含运算符下拉菜单字段的单个块。例如:

  • 内置的 logic_operation 块使用包含运算符 andor 的下拉列表。
  • 内置的 math_arithmetic 块使用包含运算符 +-×÷^ 的下拉菜单。

为此类代码块编写代码生成器通常会稍微复杂一些,但仍比编写和维护多个代码块更容易。

使用更改器

如果您有一组代表同一编程结构的不同变体的代码块,则可以创建一个使用修饰符的单个代码块。例如,内置的 controls_if 块可以表示 if-then-else 语句的多个变体。