Blockly开发入门指北

【腾讯文档】Blockly开发入门指北
https://docs.qq/doc/DRWRDUU5kR2lhaGNN

  1. 写这篇文章的目的

最近公司的项目用到了Blockly这个东西(不知道怎么形容,就用东西这个词吧),但是这个东西呢,官网没有中文,示例给的也都很简单,api文档也是杂七杂八什么都有令人头秃,国内用这个的人似乎也不多(是不是真的不多我也不清楚),网络上的相关文档很少,成体系的教程也没有,因此我打算自己写一个,出于个人水平问题,如有疏漏,在所难免。

  1. 什么是Blockly

Blockly是Google开发的一套“字符串编辑器”,我个人是这样看待的,通过少儿编程的方式实现某个功能,也就是官网上给出的那个样子,用拼图一样的东西进行拼接,通过这种方式完成代码生成。对Blockly更官方的解释,请查看百度或Blockly官网。

  1. 开发思维

为什么要有开发思维这一节呢,因为会接触Blockly二次开发的同学们应该也是程序员吧,用写代码的那种逻辑性很强、严谨感很强的思维是看不懂Blockly代码的,所以专门用这一节来描述Blockly是个什么鬼,从而掌握其开发思想。

首先看一下界面的样子,如下图所示:

下面的图片是展开一个分组之后的样子,通过上面的图片,可以看到整个界面大致分为两个部分:积木选择区和工作区,工作区的右下角是功能按钮,从上到下依次为定位、放大、缩小、删除。

在操作积木时,在左侧的积木选择区选择一个积木,然后拖拽到右侧的工作区中即可,关于积木本身的特性我们稍后细说。

下面是一个拼好一个模型的图片:

这就是一个拼好的积木模型了,这个示例中包含了很多自定义的积木块。那么它生成的代码是什么样子的呢,来看下面的图片:

这就是上面模型所生成的代码了,到这里有些有想法的同学可能会问,这个代码怎么看起来四不像呢,这就是一开始我称呼它为”字符串编辑器“而不是代码编辑器的原因所在,根据官网的介绍,Blockly支持多种语言的生成,包括python,JavaScript,php等语言,但实际上,在后面的实践过程中,我越来越理解到它并不是真正的开发代码,其本质只是一堆字符串,只不过是这些字符串符合某种编程语言的规范而已,基于这个发现,你可能会觉得这很low诶,但我认为这恰恰是Blockly的高明之处,Blockly本身支持的那些语言只是预设好了一套字符串,如果你想要支持其他的语言也不是不可以,完全可以自己进行扩展,这种看起来很low的做法,恰恰造就了Blockly跨平台的特点。

好的,通过上面的图片,我们已经知道了它生成的代码,但是这些代码在页面上是不可见的,是隐藏的背后的逻辑,那么可见的积木是什么描述的呢?

在Blockly的API中,有一项Blockly.XML的内容,是的没错,我们能够看到的这些积木,是由xml来表示的,下面看一下这个xml长什么样子:

好的,看不太懂,不过这没关系,因为这个xml只是为了告诉Blockly工作区中应该渲染什么,如何渲染,在实践的过程也只是在数据库中存了一下,用于下次打开时候的回显,并不牵涉任何业务。在这个XML中,我们看到了它只规定了应该渲染什么,工作区中都有什么,但并没有规定每个积木应该长什么样子,比如它是什么颜色,文字是什么?

积木的颜色,积木上面的文字等这些信息,了解面向对象开发的同学可能比较敏感,因为它们都属于积木块自身的属性,外观属性,这些外观属性一般是静态的,当然,在文档中也要求是静态的,这是因为这些积木渲染的时机很早,在Blockly界面初始化的时候,这些外观定义就要被用到,如果存在动态的外观属性,比如通过接口获取某个下拉框的选项,那么你需要在Blockly界面初始化之前就保证数据已经准备完备,这个数据的获取时机,需要慎重考虑,要防止取不到值导致工作区加载出问题,也要规避请求时间过长阻塞Blockly界面初始化导致的白屏。

那么到现在为止,我们已经了解了构成Blockly的几大要素,首先是积木选择区,要想使用自定义的积木,我们必须将它注册到积木选择区中,否则我们是无法使用到它的;其次是工作区渲染,即上面的XML,它是对工作区需要渲染哪些组件的说明书,然后是积木的外观定义,告诉Blockly,这个积木块应该长什么样子,外观如何;最后是代码生成,将每块积木所对应的代码,按照积木拼接的规则组合在一起。

OK,现在我们已经了解了Blockly是如何工作的,由哪几部分组成,接下来就可以尝试开发自己的积木了。

  1. 开发准备

首先在项目中安装Blockly,按照官方github上的描述,执行npm install blockly命令。

安装完成后,我们就可以在要使用到的页面中引入Blockly,引入代码如下所示:

import Blockly from 'blockly';

 

然后我们开始初始化Blockly界面,不得不说官方文档写的真是一言难尽,我在官网的Fixed-sized Workspace这一节里才摸到门路。

首先写一个div,指定要放置界面的DOM节点,给它一个id,再赋予它宽和高,然后在js中加载Blockly,按照官网的示例代码:

Add an empty div somewhere in the page's body and set its size:

【在页面的什么地方添加一个空的div并设置尺寸】

<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>

Add the structure of the toolbox (see Defining the Toolbox for more information) anywhere on the page:

【在页面的任何位置添加工具栏的结构(PS:任何位置,感觉好随便)】

<xml id="toolbox" style="display: none">

<block type="controls_if"></block>

<block type="controls_repeat_ext"></block>

<block type="logic_compare"></block>

<block type="math_number"></block>

<block type="math_arithmetic"></block>

<block type="text"></block>

<block type="text_print"></block>

</xml>

好一个display:none,这个东西就是为了不让它直接展示,而是传递给Blockly使用,我当时还特地把它删了看看啥效果。

Finally, call the following to inject Blockly into an empty div. This script should be at the bottom of the page, or called by the onload event.

【最后,在空div上调用Blockly inject方法,这个脚本必须在页面底部,或者在onload事件执行】

<script>

var workspace = Blockly.inject('blocklyDiv',

{toolbox: document.getElementById('toolbox')});

</script>

到这里初始化的工作就基本上结束了,有一个注意点就是inject这个方法,来看一下api介绍:

该方法接收两个参数,第一个是必填项,指定界面的渲染元素,可以是元素,它的id,或者一个CSS选择器,参数类型可以为元素或字符串;第二个参数是可选的,接收一个选项对象。

下面是我的真实代码:

现在我们就有了一个基础的界面,在inject的时候,可以指定Blockly的一些全局配置,在官方文档中介绍的还是比较详细的:

在实际使用过程中,大部分选项都可以保持默认,如果想要一些不一样的东西,可以自行设置参数。在以上的配置项中,有一个配置项是比较重要的,就是toolbox这个配置项,该配置项接收参数类型为XML Nodes or string,Tree structure of categories and blocks available to the user. See defining the toolbox for more information.

这段描述的大意是:提供给用户的块和分类的树形结构,更多信息查看“工具栏定义”。

这表示这个配置项指定了界面左侧的积木选择区所展示的内容,点击这个链接将会跳转到Toolbox一节,在这一节中提供了两种定义的方式,分别是XML与Json,我自己使用的是XML,下面还有工具栏的一些样式配置,分类配置等,非常详细,感兴趣的话可以自行了解。

在完成以上这些工作之后,我们发现现在工具栏中的积木都是Blockly内置好的,那么接下来,我们开始正式的写一个自定义积木。

  1. 定义一个最简单的积木

通过上面的了解,我们知道要想使用一个自定义积木,必须要在工具栏中注册这个积木,注册的前提就是你必须先有一个积木(这是废话,凑字数的),

现在我们参考官网上Add Custom Blocks一节来进行定义,通览这篇文档,可以看到积木定义大概分三步,首先定义积木,提供Json和JavaScript两种方式定义,我个人使用的是后者,为了方便以后的功能开发。定义好之后进行引用,最后指定其生成的代码。

现在我们一步一步来做,首先在你喜欢的位置创建一个js文件,用于定义我们的第一个积木,根据程序员的传统美德,我把它其名为HelloWorld.js,并复制上官网的代码,不要忘记在首行添加import语句。

分析这段代码,从第二行看起,Blockly.Blocks['string_length']={...},这是一个明显的赋值语句,对Blockly.Blocks这个对象,扩展了名为string_length的属性,并对该属性赋值为一个对象,看到这里,我们联想一下之前看到过的那个看不太懂的XML描述内容,在block节点中,不是有一个type属性吗,这个type属性指定了这个块的类型,类型的名字,就是这个Blockly.Blocks中某个属性的名字,通过这个属性的名字,就可以获取到对应积木的描述信息,好的,为了避免与内置积木重名,我们来修改一下这个属性名,换一个高大上的名字。

怎么样?是不是有了高大上的感觉呢?

下面进行第二步,注册积木。

按照文档给出的示例代码,我们给它安排!

(使用模块化开发的同学不要忘记import哈)

然后是激动人心的最后一步啦,同样按照文档给出的示例代码:

看第14行,是不是很眼熟的写法,没错,就是和上面的是一个套路,扩展一个内部对象,所以后面的属性名当然也要修改成和上面一致的啦。这里有一个注意点,就是前面的JavaScript,这个不是固定的写法哦,JavaScript表示这是用于生成JavaScript代码时执行的方法,如果想要获得其他语言的代码,比如Python,那么就要把JavaScript改成Python才可以,如果对不上的话,在获取生成的代码时会出错哦。

到此为止,我们的第一个自定义积木就写好啦,是不是非常简单?现在查看一下运行的效果:

为了更有效的看清它,我们换一个喜庆的颜色:

按照api的指引,该方法也支持RGB颜色,所以修改setColour()方法里面的参数,

修改为一个你喜欢的颜色,看一下运行效果:

非常的喜庆!

  1. 分析代码

简单的积木已经有了,大体的开发流程想必也心中有数了,现在来回顾一下积木的定义:

在解读以上代码之前,首先要了解一下积木的外观是怎么来的,在文档中的Define Blocks一节中,可以看到详细的介绍。

通过右侧的索引,看到一个积木的外观大致分为Statement Connections(语句连接器)、Output(输出/返回值)、Input(输入/参数)、Fields(字段)几个基本要素,联想一下在平时我们是如何定义一个方法的,有方法名、参数列表(参数名、参数类型)、返回类型,对应到积木的外观定义,就很明显了。现在回过头看上面的积木定义代码,是不是很清楚了?

再看下面生成代码的部分,它扩展了一个函数,并返回要生成的代码,在示例代码中,首先获取了一下被输入的参数,也就是上面命名为‘VALUE’的ValueInput中得到的内容,valueToCode方法顾名思义就是将输入的值转换为代码的方法,第一个参数为积木本身,即被执行该方法的对象,第二个参数是要取值的字段名,第三个参数是执行的顺序,在多个积木并行连接的时候有用,一般默认为0就好。

在文档的介绍中,可以看到有三种获取参数的方式,分别对应获取Value、获取Field和获取语句块,在文档中有详细的介绍,不再赘述。

值得注意的是最后的return语句,return的时候分为两种情况,一种情况是返回数组,数组中第一个元素是返回的代码语句,第二个为计算顺序,一般默认为0。另一种情况是直接返回一个字符串作为代码语句,如下面的例子:

这两种情况,主要看这个积木有没有返回值,如果没有返回值,那么说明这是一个语句块,不可能作为另一个积木的参数存在,所以直接返回字符串即可,如果有返回值,那么说明这是一个值块,即有可能作为另一个积木的参数或者输入值出现,所以需要给定一个计算顺序,返回一个数组。

入门的教程到这里就结束了,在实践的过程中,还会有更加深入的使用。

 

 

更多推荐

Blockly开发入门指北