“块定义”描述块的外观和行为,包括:文本、颜色、形状以及可连接到的其他块。定义块以后,这些自定义块可以不同的平台加载。
1. JSON格式与JavaScript格式API
Blockly 提供了两种形式的块定义API:JSON格式和JavaScript格式。其中,JSON格式是跨平台的,这样代码就可以在Web、Android、和iOS平台上使用。另外,JSON格式旨在简化针对不同语言时的国际化流程。所以,定义块时首选JSON格式。
但是,JSON格式无法定义高级功能,如:mutator或validator。定义复杂功能的块时需要使用JavaScript API。
JSON
{
"type": "string_length",
"message0": 'length of %1',
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": "String"
}
],
"output": "Number",
"colour": 160,
"tooltip": "Returns number of letters in the provided text.",
"helpUrl": "http://www.w3schools.com/jsref/jsref_length_string.asp"
}
JavaScript
Blockly.Blocks['string_length'] = {
init: function() {
this.appendValueInput('VALUE')
.setCheck('String')
.appendField('length of');
this.setOutput(true, 'Number');
this.setColour(160);
this.setTooltip('Returns number of letters in the provided text.');
this.setHelpUrl('http://www.w3schools.com/jsref/jsref_length_string.asp');
}
};
以上两种方式都定义了一个相同的"string_length"块:

在Web环境下,JSON格式定义的块可以通过initJson函数来加载,也可以混用两种格式的块包。应该尽可能的使用JSON定义块,并仅在JSON不支持的块定义部分使用JavaScript。
以下是是一个块定义示例,其主要使用JSON定义块,但使用JavaScript API进行了扩展以提供动态工具提示:
var mathChangeJson = {
"message0": "change %1 by %2",
"args0": [
{"type": "field_variable", "name": "VAR", "variable": "item", "variableTypes": [""]},
{"type": "input_value", "name": "DELTA", "check": "Number"}
],
"previousStatement": null,
"nextStatement": null,
"colour": 230
};
Blockly.Blocks['math_change'] = {
init: function() {
this.jsonInit(mathChangeJson);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return 'Add a number to variable "%1".'.replace('%1',
thisBlock.getFieldValue('VAR'));
});
}
};
2. 块颜色
JSON
{
// ...,
"colour": 160,
}
JavaScript
init: function() {
// ...
this.setColour(160);
}
Blockly使用Hue-Saturation-Value(HSV)颜色模式。饱合度(Saturation)和值(Value)被硬编码到Blockly中,这样开发者可以自由选择各种颜色,并保证所有块都在在视觉上一致性。通过简单地调整两个硬编码的饱和度和值常数,能够使整个Blockly的外观变的鲜艳或暗淡:

您可以能过HSV 颜色选择器找到适合自己的颜色,在Blockly中输入饱和度、和相应的值常数(默认值分别为:45%、65%),然后根据需要滑动色块,并将相应的值做为this.setColour函数的参数,即可设置成功。
3. 语句连接
用户通过nextStatement和previousStatement连接器可以创建一系列的块。在 Blockly 的标准布局中,这些连接位于顶部和底部,并且垂直堆叠。
块的previous连接器不能是“输出”连接器,反之亦然。术语语句块是指没有值输出的块。语句块通常将具有先前的连接和下一个连接。“术语”块是指没有值输出的块,“语句”块通常有previous和next连接。
nextStatement和previousStatement连接可以指定类型,但这一特性不能用在标准块中。
下一步(Next)连接

在块的底部创建一个凸点,就可以在其下方堆叠其他语句。具有next连接但没有previous连接的块通常表示事件,并且可以配置为以顶点呈现。
JSON
// Untyped
{
...,
"nextStatement": null,
}
// Typed
{
"nextStatement": "Action",
...
}
JavaScript
// Untyped this.setNextStatement(true); // false implies no next connector, the default // Typed this.setNextStatement(true, 'Action');
上一步(Previous)连接

在块的顶部创建一个入口(凹口),以便它可以作为一个堆语句进行连接。具有previous连接的块不能有输出连接。
JSON
// Untyped
{
...,
"previousStatement": null,
}
// Typed
{
"previousStatement": "Action",
...
}
JavaScript
// Untyped this.setPreviousStatement(true); // false implies no previous connector, the default // Typed this.setPreviousStatement(true, 'Action');
4. 块输出(output)

块可以有单个输出,表示为拼图边缘的连接器接口,其“输出”会连接到“值”输入。 具有输出的块通常称为“值”(value)块。
JSON
// Untyped
{
// ...,
"output": null,
}
// Typed
{
// ...,
"output": "Number",
}
JavaScript
// Untyped
init: function() {
// ...
this.setOutput(true);
}
// Typed
init: function() {
// ...
this.setOutput(true, 'Number');
}
有output连接器的块,同时还可以previous(即:缺口)
5. 块输入(input)
输入块可以有一个或多个输入,其中每个输入是可以在连接中结束的一系列标签和字段。根据连接类型,有三种类型的输入:
- 值(Value)输入:连接到值块的输出连接。 如:数学运算块(加法,减法)是具有两个值输入的块。
- 语法(Statement)输入:连接到语句块的
previous连接。如: while循环的嵌套部分。 - 虚拟(Dummy)输入:没有块连接。如:换行符,当块被配置为使用外部值输入时使用。

JSON中的输入与字段
JSON定义的块被构造为内置消息串(message0、message1、...)的序列,其中每个“插值令牌”(%1、%2、...)是字段或输入端(因此,输入连接器 在消息中渲染)在匹配的JSONargsN数组中。 这种格式旨在使国际化更容易。
{
"message0": "set %1 to %2",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "item",
"variableTypes": [""]
},
{
"type": "input_value",
"name": "VALUE"
}
]
}

插值令牌必须完全匹配args0数组:不重复、不遗漏。 令牌可以以任何顺序存在,这允许不同的语言改变块的布局。
插值令牌任一侧的文本都被空白修剪。使用字符%的文本(例如,当引用百分比时)应使用%%,以使其不被转义为内插令牌。
参数和参数类型的顺序定义块的形状。更改其中一个字符串就可以完全改变块的布局。这对于与英语不同的单词顺序的语言尤其重要。在使用时应该注意这一点。

Blockly 会自动更改字段的顺序、创建虚拟输入、并从外部输入切换。
Args
每个消息字符串与相同数字的args数组配对。 如:message0与args0一起使用。插值令牌(%1,%2,...)指的是args数组的项目。每个对象都有一个type字符串,其余的参数因类型而异:
- 字段:
- field_input
- field_dropdown
- field_checkbox
- field_colour
- field_number
- field_angle
- field_variable
- field_date
- field_label
- field_image
- 输入
- input_value
- input_statement
- input_dummy
每个对象都有一个alt字段。当Blockly 没有识别出type,这时就会使用alt对象替换。如:
{
"message0": "sound alarm at %1",
"args0": [
{
"type": "field_time",
"name": "TEMPO",
"hour": 9,
"minutes": 0,
"alt":
{
"type": "field_input",
"name": "TEMPOTEXT",
"text": "9:00"
}
}
]
}
如果消息字符串以不包含输入的文本或字段结尾,则虚拟输入将自动添加到块的末尾。因此,如果块上的最后一个输入是虚拟输入,那么它可以从args数组中省略,并且不需要插入到消息中。 拖尾虚拟输入的自动添加允许编译器更改消息,而无需修改其余的JSON。
lastDummyAlign0
在少数情况下,自动创建的尾部虚拟输入需要使用RIGHT或CENTER对齐。如果未指定,则默认为LEFT。
在以下示例中,message0是"send email to %1 subject %2 secure %3"。Blocly 会自动添加第三行的虚拟输入。设置lastDummyAlign0为RIGHT会强制将行右对齐。

当设计RTL块时
message1, args1, lastDummyAlign1
一些块自然分为两个或更多个独立的部分。考虑这个有两行的重复块:

当块被设计为单消息时,message0会"repeat %1 times %2 do %3"。这个字符串对于解析器来说会有歧义,因为很难理解%2替代的意思。在有些语言中,甚至不需要%2虚拟输入,并且可能有多个块希望共享第二行的文本。 更好的方法是让JSON使用多个消息和参数属性:
{
"type": "controls_repeat_ext",
"message0": "repeat %1 times",
"args0": [
{"type": "input_value", "name": "TIMES", "check": "Number"}
],
"message1": "do %1",
"args1": [
{"type": "input_statement", "name": "DO"}
],
"previousStatement": null,
"nextStatement": null,
"colour": 120
}
任何数量的message、args、lastDummyAlign都可以以JSON格式定义,定义从0开始递增。
JavaScript中的输入与字段
在JavaScript的API中每个输入类型都包含一个append方法:
this.appendDummyInput()
.appendField('for each')
.appendField('item')
.appendField(new Blockly.FieldVariable());
this.appendValueInput('LIST')
.setCheck('Array')
.setAlign(Blockly.ALIGN_RIGHT)
.appendField('in list');
this.appendStatementInput('DO')
.appendField('do');

每个方法都可以使用代码生成器所使用的标识符字符串。虚拟输入很少需要引用,标识符通常不设置。每个方法通过方法链接返回用于配置的输入对象。 有三个功能用于配置输入。
setCheck
input.setCheck('Number');
这是一个可选功能,用于连接输入类型的检查。 如果给出一个null参数(默认),则该输入可能连接到任何块。 有关细节,请参阅类型检查。
setAlign
input.setAlign(Blockly.ALIGN_RIGHT);
这个可选功能用于对齐字段。其有三个自描述值可以作为参数传递给此函数:Blockly.ALIGN_LEFT、Blockly.ALIGN_RIGHT和Blockly.ALIGN_CENTRE。注意,当设计 RTL 块时,左右颠倒。
appendField
一旦创建了一个输入并将其附加到具有appendInput的块中,就可以将任意数量的字段附加到输入。 这些字段通常用作标签来描述每个输入:
input.appendField('hello');

最简单的字段元素是文本。Blockly 的惯例是使用小写字母命名,名称除外(如:Google,SQL)。
输入行中可以包含任意数量的字段元素。多个appendField调用可以链接在一起,以便将多个字段添加到同一个输入行。
input.appendField('hello')
.appendField(new Blockly.FieldLabel('Neil', 'person'));

appendField('hello')调用实际上是使用一个显式的FieldLabel构造函数的快捷方式:appendField(new Blockly.FieldLabel('hello'))。
内置与外置
Blockly 可以内置或外置方式呈现。

块定义可以指定一个可选的布尔值来控制是否内联输入。如果为false,那么任何值输入将是外部的(如:上面左边的块)。 如果为true,那么任何值输入将是内联的(如:上面右侧的块)。
JSON
{
// ...,
"inputsInline": true
}
JavaScript
init: function() {
// ...
this.setInputsInline(true);
}
6. 字段
字段定义块中的UI元素。这些包括字符串标签、图像和文字数据(如:字符串和数字)的输入。最简单的例子是math_number块,一个field_input(web)或field_number(Android)来输入一个数字。

标签(Label)
标签字符串为其他字段和输入提供上下文。
{
"type": "example_label",
"message0": "a label %1 and another label",
"args0": [
{
"type": "input_dummy"
}
]
}
插补参数之间的任何消息文本都成为标签字符 或者标签可以被显式地内插,作为对象或者文本。
{
"type": "example_label",
"message0": "%1 %2 %3",
"args0": [
{
"type": "field_label",
"text": "a label"
},
{
"type": "input_dummy"
},
"and another label"
]
}
JavaScript
Blockly.Blocks['example_label'] = {
init: function() {
this.appendDummyInput()
.appendField(new Blockly.FieldLabel('a label'));
this.appendDummyInput()
.appendField('and another label');
}
};
appendField接受FieldLabel和更常见的字符串来创建标签。

当明确添加时,作者可以包括一个CSS样式名称,以自定义Blockly for Web上的视图。
JSON
{
"type": "example_styled_label",
"message0": "%1",
"args0": [
{
"type": "field_label",
"text": "Styled label",
"class": "style-name"
}
]
}
JavaScript
Blockly.Blocks['example_styled_label'] = {
init: function() {
this.appendDummyInput()
.appendField(new Blockly.FieldLabel('Styled label', 'style-name'));
}
};

图片(image)
像标签一样,图片通常是静态指示符,而不是输入字段。
JSON
{
"type": "example_image",
"message0": "image: %1",
"args0": [
{
"type": "field_image",
"src": "https://www.gstatic.com/codesite/ph/images/star_on.gif",
"width": 15,
"height": 15,
"alt": "*"
}
]
}
JavaScript
Blockly.Blocks['example_labels'] = {
init: function() {
this.appendDummyInput()
.appendField("image:")
.appendField(new Blockly.FieldImage("https://www.gstatic.com/codesite/ph/images/star_on.gif", 15, 15, "*"));
}
};
使用图像URL和所需的高度和宽度创建FieldImage对象,图像会被缩放到指定的尺寸内,同时保留宽高比。该块将根据需要扩展以适应图像。可选的第四个参数指定块被折叠时使用的替代文本。
图片的URL可以是相对的。在Web中,可以通过Blockly.pathToBlockly来指定根目录,而在Android中可以将相关URL做为资源路径(asset path)。
文本字段
文本字段允许用户输入文本内容。
JSON
{
"type": "example_textinput",
"message0": "text input: %1",
"args0": [
{
"type": "field_input",
"name": "FIELDNAME",
"text": "default text"
}
]
}
JavaScript
Blockly.Blocks['example_textinput'] = {
init: function() {
this.appendDummyInput()
.appendField("text input:");
.appendField(new Blockly.FieldTextInput('default text'),
'FIELDNAME');
}
};

可选地,JavaScriptFieldTextInput构造函数也可以有第二个参数这样会使其成为验证函数或更改处理器。
默认情况下,文本输入会进行拼写检查,但可以选择关闭:
JSON
{
"type": "example_nospellcheck",
"message0": "no spell check: %1",
"args0": [
{
"type": "field_input",
"name": "FIELDNAME",
"text": "default text",
"spellcheck": false
}
]
}
JavaScript
Blockly.Blocks['example_nospellcheck'] = {
init: function() {
var textInput = new Blockly.FieldTextInput('default text');
textInput.setSpellcheck(false);
this.appendDummyInput()
.appendField("text input:");
.appendField(textInput, 'FIELDNAME');
}
};
下拉字段
JSON
{
"type": "example_dropdown",
"message0": "drop down: %1",
"args0": [
{
"type": "field_dropdown",
"name": "FIELDNAME",
"options": [
[ "first item", "ITEM1" ],
[ "second item", "ITEM2" ]
]
}
]
}
JavaScript
Blockly.Blocks['example_dropdown'] = {
init: function() {
this.appendDummyInput()
.appendField('drop down:');
.appendField(new Blockly.FieldDropdown([
['first item', 'ITEM1'],
['second item', 'ITEM2']
]),
'FIELDNAME');
}
};

下拉列表的列表项由两部分列表的列表指定。 每个内部列表的第一个项目是人类可读的名称; 第二个是项目标识符字符串。
在 Web Blockly 中,如果所有的人类可读字符串共享一些前缀或后缀,则这些字符串将在下拉列表之前或之后提取为标签。这允许前缀/后缀标签位置适应语言约定。
Checkbox字段
Checkbox 字段提供了一个布尔输。

JSON
{
"type": "example_checkbox",
"message0": "checkbox: %1",
"args0": [
{
"type": "field_checkbox",
"name": "FIELDNAME",
"checked": true
}
]
}
JavaScript
Blockly.Blocks['example_checkbox'] = {
init: function() {
this.appendDummyInput()
.appendField('checkbox:');
.appendField(new Blockly.FieldCheckbox('TRUE'), 'FIELDNAME');
}
};
可选的,FieldCheckbox构造函数还可以使用第二个参数,该参数将成为验证函数或更改处理器。
颜色选择器字段
颜色选择器允许用户从所提供的一组不透明颜色中进行选择。

JSON
{
"type": "example_colour",
"message0": "colour picker: %1",
"args0": [
{
"type": "field_colour",
"name": "FIELDNAME",
"colour": "#ff0000"
}
]
}
JavaScript
Blockly.Blocks['example_colour'] = {
init: function() {
this.appendDummyInput()
.appendField('colour picker:');
.appendField(new Blockly.FieldColour('#ff0000'), 'FIELDNAME');
}
};
可选的,FieldColour构造函数还可以使用第二个参数,该参数将成为验证函数或更改处理器。
在Web Blockly中,可以全局设置所有颜色选择器中显示的颜色和列数选择:
Blockly.FieldColour.COLOURS = ['#f00','#0f0','#00f','#000','#888','#fff']; Blockly.FieldColour.COLUMNS = 3;

或者,单独的颜色选择器也可以被定制选择:
var colour = new Blockly.FieldColour('#ff0000');
colour.setColours(['#f00','#0f0','#00f','#000','#888','#fff']).setColumns(3);
input.appendField(colour, 'COLOUR');
变量字段
另一个字元素是可变选择菜单。 该字段定义一些要使用的默认变量名称(以下示例中为'x')。 如果省略此名称,该变量将是一个新的唯一变量(如:'i','j','k'...)。

JSON
{
"type": "example_variable",
"message0": "variable: %1",
"args0": [
{
"type": "field_variable",
"name": "FIELDNAME",
"variable": "x"
}
]
}
JavaScript
Blockly.Blocks['example_variable'] = {
init: function() {
this.appendDummyInput()
.appendField('variable:');
.appendField(new Blockly.FieldVariable('x'), 'FIELDNAME');
}
};
数字(Number)字段
数字字段提供了数字输入及验证。

JSON
{
"type": "example_number",
"message0": "number: %1",
"args0": [
{
"type": "field_number",
"name": "FIELDNAME",
"value": 100
}
]
}
JavaScript
Blockly.Blocks['example_number'] = {
init: function() {
this.appendDummyInput()
.appendField("number:");
.appendField(new Blockly.FieldNumber('100'), 'FIELDNAME');
}
};
使用附加参数,可以通过多种方式限制数字输入。范围可以由mix和max约束。设置precision(通常为10的幂)可以设置值之间的最小步长。
JSON
{
"type": "example_byte",
"message0": "%1",
"args0": [
{
"type": "field_number",
"name": "VALUE",
"value": 0,
"min": -128,
"max": 127,
"precision": 1
}
]
}
JavaScript
Blockly.Blocks['example_byte'] = {
init: function() {
this.appendDummyInput()
.appendField(new Blockly.FieldNumber('0', -128, 127, 1), 'FIELDNAME');
}
};
可选的,FieldNumber构造函数还可以使用第五个参数,该参数将成为验证函数或更改处理器。
角度(Angle)字段
角度字段提供了一个UI,可以图形化的选择角度值:

默认情况下,从0°(包括)到360°(独占)逆时针,向右0°,顶端90°。 超出绑定的值被包装到这个刻度上。使用键盘可以输入任何角度(均匀分数),但用鼠标选择的角度将舍入为15°。
JSON
{
"type": "example_angle",
"message0": "angle: %1",
"args0": [
{
"type": "field_angle",
"name": "FIELDNAME",
"angle": "90"
}
]
}
JavaScript
Blockly.Blocks['example_angle'] = {
init: function() {
this.appendDummyInput()
.appendField('angle:');
.appendField(new Blockly.FieldAngle(90), 'FIELDNAME');
}
};
可选的,FieldAngleInput构造函数还可以使用第二个参数,该参数将成为验证函数或更改处理器。
日期(Date)字段

JSON
{
"type": "example_date",
"message0": "date: %1",
"args0": [
{
"type": "field_angle",
"name": "FIELDNAME",
"date": "2015-02-05"
}
]
}
JavaScript
Blockly.Blocks['example_date'] = {
init: function() {
this.appendDummyInput()
.appendField('date:')
.appendField(new Blockly.FieldDate('2015-02-05'), 'FIELDNAME');
}
};
FieldDate提供了日历日期。它被初始化为特定的日期,如果没有指定日期,则是今天的日期。
可选的,FieldDate构造函数还可以使用第二个参数,该参数将成为验证函数或更改处理器。
注意:由于其使用有限和占用空间大,缺省情况下FieldDate不会被编入Blockly。 将goog.require('Blockly.FieldDate')添加到项目中以启用它。
7. 提示(Tooltips)
当用户将鼠标悬停在块上时,工具提示即时提供帮助。 如果文本很长,它将自动包装。
JSON
{
// ...,
"tooltip": "Tooltip text."
}
JavaScript
init: function() {
this.setTooltip("Tooltip text.");
}
在JavaScript API中,工具提示可以定义为函数替代静态字符串,这样可以实现动态提示。如:
Blockly.Blocks['math_arithmetic'] = {
init: function() {
// ...
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('OP');
var TOOLTIPS = {
'ADD': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
'MINUS': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
'MULTIPLY': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
'DIVIDE': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
'POWER': Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER
};
return TOOLTIPS[mode];
});
}
};
8. 帮助URL
块可以有一个与它们相关联的帮助页面。Web Blockly 用户可以通过右键单击该块并从上下文菜单中选择“帮助”。 如果该值为null,那么菜单将变灰。
JSON
{
// ...,
"helpUrl": "https://en.wikipedia.org/wiki/For_loop"
}
JavaScript
init: function() {
// ...
this.setHelpUrl('https://en.wikipedia.org/wiki/For_loop');
}
使用JavaScript API,可以指定一个函数,而不是静态URL字符串,从而实现动态帮助。
9. 修改监听与验证器
块可以有更改监听器函数,这些功能可以在工作空间的任何更改(包括与块无关的变量)时被调用。主要用于在工作区外设置块的警告文本或类似的用户通知。
该函数通过调用setOnChange函数来添加,并且可以在init或通过JSON扩展完成。
JSON
{
// ...,
"extensions":["warning_on_change"],
}
Blockly.Extensions.register('warning_on_change', function() {
// Example validation upon block change:
this.setOnChange(function(changeEvent) {
if (this.getInput('NUM').connection.targetBlock()) {
this.setWarningText(null);
} else {
this.setWarningText('Must have an input block.');
}
});
});
JavaScript
Blockly.Blocks['block_type'] = {
init: function() {
// ...
setOnChange(function(changeEvent) {
if (this.getInput('NUM').connection.targetBlock()) {
this.setWarningText(null);
} else {
this.setWarningText('Must have an input block.');
}
});
}
}
系统会调用该函数,并传入change事件。 在函数内部,会引用到块实例。
由于任何更改都会调用该函数,所以开发者应确保监听器能够快速运行。并应注意对工作区修改可能的会有级联或产生回环调用。
10. Mutator
Mutator 允许高级块修改形状,最明显的是用户打开对话框以添加、删除或重新排列组件。Mutator 可以通过JSON和mutator键来添加。
JSON
{
// ...,
"mutator":"if_else_mutator"
}
11. 块配置
块实例有很多属性用于配置它们的用户行为。这些配置可用于约束工作区引用域的具体属性、强制一些用户行为等。
可删除状态(Deletable State)
默认情况下,用户可以删除可编辑工作区上的任何块(非readOnly)。 有时需要让某些块永久固定(例如,一些骨架代码)。
这时可以设置如下:
block.setDeletable(false);
任何块,包括那些标记为不可删除的块,都可以通过编程方式删除:
JavaScript(Web)
block.dispose();
Java(Android)
blocklyController.removeBlockTree(block);
可编辑状态(Editable State)
block.setEditable(false); // Web or Android
设置为flase时,块字段将不能被修改。在可编辑工作区中,块默认为可编辑状态。
可移动状态(Movable State)
block.setMovable(false); // Web or Android
设置为flase时,块将不能被直接移动。作为另一块的子块,设置为不可移动块时其将不可父块断开连接,但父块被移动,它将与其父块一起移动。
在可编辑工作区中,块默认为可移动状态。
任何块(包括不可移动块)都可以在工作区上以编程方式移动。在JavaScript环境下,通过调用block.moveBy(dx, dy)实现。除非特别说明,否则工作区上块的起始位置默认为(0,0)。
块数据(仅 Web)
this.data = '16dcb3a4-bd39-11e4-8dfc-aa07a5b093db'; // Web only
数据是可选的附加到块的任意字符串。 当以XML格式保存时,数据字符串存储在<data></data>标签中,以便它可以返回到一个块。使用数据字符串可以将块与外部资源相关联或用于任何其他自定义目的。
