修改區塊定義

常見的問題是如何修改現有區塊的定義。舉例來說,您可能想新增連線檢查,或將欄位變更為值輸入內容。

一般來說,您無法在原地修改區塊定義。

如何修改現有定義

如要修改現有區塊類型的定義:

  1. 複製現有定義,包括區塊程式碼產生器。
  2. 修改副本,並為類型命名。
  3. 將新定義加入 Blockly.Blocks

為什麼我無法直接修改現有定義?

如果您想瞭解為何無法修改現有定義,請繼續閱讀。我們會考慮一些可能性。

直接修改現有定義

您可以透過兩種方式直接修改現有區塊的定義:猴子修補和分支程式碼。我們強烈建議不要使用這兩種方法,因為這麼做可能會破壞依賴猴子補丁或分支程式碼的程式碼。這兩種做法也會使整合更新和錯誤修正變得困難。詳情請參閱「monkeypatching 有何影響?」和「分支 Blockly」。

將現有定義設為子類

您可能會想以某種方式為現有定義建立子類別。很抱歉,這並非可行做法,因為區塊定義並非類別,而是混合物。舉例來說,定義中沒有顏色屬性,因此無法覆寫顏色屬性。而是使用 init 函式呼叫 setColour,藉此設定區塊的顏色屬性。由於呼叫位於 init 中,因此如果不替換整個 init 函式,就無法替換呼叫。

覆寫現有定義中的函式

您可以在現有定義中覆寫函式:

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

這麼做是可行的,但您必須複製及修改現有函式,因為您無法以神奇的方式只取代一行。這種做法會產生以下問題:

  • 這與複製及修改整個定義沒有太大差異。
  • 您無法使用這個函式修改 JSON 中定義的區塊 init 函式,例如 Blockly 內建的區塊。這是因為沒有要複製的 init 函式,而是在執行期間產生。
  • 您必須使用 JavaScript 定義區塊,這可能會導致本地化問題

覆寫初始化的結果

如要「只取代 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 陳述式的變化。