引言

前些日子google刚发布了blockly1.0,社区人气一路上升,我集中时间逛了下开发者社区(developer forum),发现好些有意思的开源项目,有不少我手边的工作都用得到,能省下许多的时间,逛开发者社区常有意外的惊喜,比刷朋友圈有趣多了

下文我们将提到的socketblockly便来自社区

结对编程

按维基百科的说法

结对编程(英语:Pair programming)是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。一个人输入代码,而另一个人审查他输入的每一行代码。输入代码的人称作驾驶员,审查代码的人称作观察员

我个人很喜欢结对编程。甚至远程协作的时候,我也喜欢和朋友一起登陆到服务器,一起进入同个tmux session(你可能更喜欢wemux,如果你爱折腾甚至可以试试CoVim),手边打开微信语音,编程相关的内容,大家的屏幕是彼此同步的,输入也完全同步,沟通效率远胜于任何截图(甚至好过一起聚在同个屏幕面前)。对服务器的重要的操作彼此监督,不容易出错。在工作交接的时候,这种协作方式效率也极高。甚至在公司,我也喜欢以这种方式和同事协作,一群人挤在一个屏幕面前,很不自在。共享屏幕并不需要在同个物理空间

我将结对编程视为协同工作的一种,协同工作就宽泛得多了,我比较关注在线实时的协同工具(对远程办公有极大好感:) ),能够对此提供支持的工具有很多,诸如google文档石墨文档togetherjs

这些能用来进行协同工作的工具,也适合用来做远程教学(手把手),当然我们可能需要区分学生和老师的角色,这个是细节问题

blockly与结对编程

在blockly中我们如何实现结对编程呢?(对blockly不熟悉的小伙伴可以参考blockly入门与介绍)

这是个很有趣的话题,乍看之下blocks是UI,似乎比协同文本更难

我们来看看socketblockly是怎么做的

思路

socketblockly的解决方案简单粗暴,简单来说,就是观察workspace中是否有变化,如果有变化,就把blocks转变为xml(序列化之后变成普通的文本同步问题)通知出去,其他用户在本地做反序列化,用新的blocks更新本地的workspace,如此就实现了编辑的同步(也就是协同)

简而言之,如果你意识到blocks与xml是可以相互转化的,那么这件事就是一个普通的(文本)协同编辑问题

源码分析

因为有实时性的要求,主要使用了socket.io来做实时通信

server端

server端非常简单,一目了然

1
2
3
  socket.on('new xml', (xml) => {
        socket.broadcast.emit('rebuild workspace', xml)
  })

监听新消息(new xml),并将其广播给其他客户端

client端

client端也干净利索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var workspace = Blockly.inject('blocklyDiv', workspaceOptions)

function updateWorkspace (event) {
  if (event.type == Blockly.Events.UI || event.type == Blockly.Events.CREATE) return

  let xml = Blockly.Xml.workspaceToDom(workspace)
  let xml_text = Blockly.Xml.domToText(xml)

  socket.emit('new xml', xml_text)
}

workspace.addChangeListener(updateWorkspace)

socket.on('rebuild workspace', function (xml) {
	Blockly.Events.disable()
	workspace.clear()
	var xmlData = Blockly.Xml.textToDom(xml)
	Blockly.Xml.domToWorkspace(xmlData, workspace)
	Blockly.Events.enable()
})

基本就是前头思路的代码实现,workspace中的blocks转为xml通过:let xml = Blockly.Xml.workspaceToDom(workspace)

而xml还原为界面元素(blocks)通过:var xmlData = Blockly.Xml.textToDom(xml); Blockly.Xml.domToWorkspace(xmlData, workspace)

用户的编辑事件通过:workspace.addChangeListener(updateWorkspace) 捕获,并回调updateWorkspace函数.

关于blockly的事件机制可以参考:blockly events

演示

你可以在demo里自己尝试(开2个浏览器)

todo

建议将socketblockly视为一个最小原型,可以改进的地方还有很多,诸如:

  • 加上房间的概念(类似togetherjs),以支持多对用户的协作,社区里有人尝试blockly-realtime-collab
  • 加上togetherjs,使协同工作者能以代码之外的方式沟通,而不必离开当前界面(类似我同时使用tmux和微信语音)