通过本文逐步熟悉bpmn-js。
快速介绍:
bpmn.js是一个BPMN2.0渲染工具包和web建模器。使用JavaScript编写,在不需要后端服务器支持的前提下向现代浏览器内嵌入BPMN2.0流程图。这使得它很容易的嵌入到任何web应用中。
这个库既可以是web查看器也可以是web建模器。使用查看器将BPMN2.0流程图嵌入到你的应用中并可以使用数据丰富你的流程图。使用建模器在你的应用内部创建BPMN2.0流程图。
下文将向您介绍如何使用该库,并深入了解其内部结构。也就是说,这两个组件促成了该库的高度模块化和可扩展的结构。
本文包含以下内容:
● 如何使用库
• 嵌入查看器(预打包)
• 自己制作模型(通过npm)
● 理解bpmn-js内部
• 流程图交互/建模(流程图-js)
• BPMN元模型(bpmn-moddle)
• 将事物整合在一起(bpmn-js)
● 进一步探索
如何使用库
有两种方法在你的应用中使用bpmn.js。库的预打包版本可以让你在任何网站中快速添加BPMN。npm版本的设置更加复杂,但允许您访问单个库组件,并更容易扩展。
本节将概述这两种方法。首先我们来介绍如何向网站中嵌入一个预打包的BPMN查看器。之后,展示如何通过npm创建一个BPMN建模器。
嵌入预打包查看器
通过一个简单的script标签引入就可以将预打包的BPMN查看器嵌入你的网站
<!-- BPMN 流程图容器 -->
<div id="canvas"></div>
<!-- 使用 CDN 路径 或者本地 bpmn-js 路径 -->
<script src="https://unpkg.com/bpmn-js@0.27.0-1/dist/bpmn-viewer.development.js"></script>
通过引入的BPMNjs变量,可以使BPMN查看器可用。我们可以通过下面这段js来引入一个BPMN流程图。
<script>
// 你即将要展示的流程图
var bpmnXML;
// BpmnJS是BPMN查看器实例
var viewer = new BpmnJS({ container: '#canvas' });
// 导入BPMN 2.0流程图
viewer.importXML(bpmnXML, function(err) {
if (err) {
//导入失败 !
} else {
// 导入成功!
var canvas = viewer.get('canvas');
canvas.zoom('fit-viewport');
}
});
</script>
该代码片段使用Viewer#importXML API显示预加载的BPMN 2.0图。导入图是异步的,一旦完成,查看器将通过回调通知我们结果。
导入后,我们可以通过Viewer#get访问各种图表服务。在上面的代码片段中,我们与画布交互以使图适合当前可用的视图大小。
通常,通过AJAX动态加载BPMN 2.0图更实用。这可以使用纯JavaScript(如下所示)或通过实用程序库(如jQuery)实现,后者提供了更方便的api。
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
viewer.importXML(xhr.response, function(err) {
// ...
});
}
};
xhr.open('GET', 'path-to-diagram.bpmn', true);
xhr.send(null);
</script>
查看预打包的示例以及我们的初学者示例,了解更多信息。
创建属于自己的建模器
如果你想要围绕库做一些定制化,需要使用npm来使用库。这种方法有很多优点,比如可以访问单个库组件。它还使我们能够更好地控制部分打包查看器/建模器。需要使用Webpack (>=2) or Rollup来打包bpmn-js和我们的应用。
以下大致遵循modeler示例,使用该库创建BPMN建模器。
引入库
首先,通过npm安装bpmn.js
npm install bpmn-js
然后,通过ES import获得BPMN建模器
import Modeler from 'bpmn-js/lib/Modeler';
// 创建一个建模器
var modeler = new Modeler({ container: '#canvas' });
// 导入流程图
modeler.importXML(bpmnXML, function(err) {
// ...
});
同样的,需要在你的html中提供一个id声明的画布元素,以便建模器能够向画布内渲染。
添加样式
当将建模器嵌入网页时,要引入diagram-js样式以及BPMN图标字体。它们都随bpms -js发行版一起在dist/assets文件夹下提供。
<link rel="stylesheet" href="bpmn-js/dist/assets/diagram-js.css" />
<link rel="stylesheet" href="bpmn-js/dist/assets/bpmn-font/css/bpmn-.css" />
添加样式表可以确保图元素获得适当的样式化,背景板以及画板显示BPMN图标
浏览器打包
bpmn.js及其依赖由ES模块分发。使用ES模块打包器Webpack (>=2) 或者 Rollup来打包bpmn.js和你的应用。可以通过打包例子来学习更多相关。
与生命周期事件挂钩
事件允许您钩入建模器的生命周期以及图交互。下面的代码片段展示了一般情况下如何捕获元素的更改和建模操作。
modeler.on('commandStack.changed', function() {
// 建模或执行撤销/重做操作
});
modeler.on('element.changed', function(event) {
var element = event.element;
// 元素改变触发
})
使用Viewer#on注册事件或者扩展模块中的事件总线。使用 Viewer#off 来停止监听事件。请查看交互示例,以查看监听动作中的事件。
建模器的扩展
在实际使用的过程中,你可以用additionalModules选项来扩展查看器和建模器。允许您使用自定义模块来修改或替换现有的功能。
import OriginModule from 'diagram-js-origin';
// 创建一个建模器
var modeler = new Modeler({
container: '#canvas',
additionalModules: [
OriginModule,
require('./custom-rules'),
require('./custom-context-pad')
]
});
一个模块(比较:模块系统部分)是一个单元,定义了一个或多个服务。这些服务配置了bpmn.js或者提供了额外的功能。也就是通过接入流程图的生命周期来实现的。
一些模块,例如: diagram-js-origin 或 diagram-js-minimap提供了通用的用户界面添加。内置的bpmn-js模块,例如:such as bpmn rules 或 modeling提供了高定制的BPMN功能。
常用扩展BPMN建模器的方法是添加“自定义建模规则”。这样,您可以限制或扩展用户的建模操作。
扩展的其他例子有:
● 添加自定义元素
● 自定义面板/内容面板
● 自定义形状的渲染
查看bpms -js-example项目,了解更多工具包扩展展示案例。
构建自定义发行版
如果你想要针对你自定义的建模器和查看器创建预打包版本,请参 custom-bundle example这个例子。如果你定制了大量的功能但是希望以简单的方式交付给用户的话,这可能会给到你帮助。
理解bpmn-js内部
本节探讨一些bpmn-js的内部结构。
如下面的架构图所示,bpmn-js构建在两个重要的库之上:diagram-js和bpmn-moddle。
我们使用diagram-js 来绘制形状和连接。它为我们提供了与这些图形元素交互的方法,同时也提供了额外的工具例如overlays来帮助用户构建更为强大的查看器。对于更高级的建模情况,提供了提供了背景板、调色板和重做/撤消等功能。
bpmn-moddle清楚BPMN2.0元模型,元模型定义在BPMN2.0规范之上。它允许我们读写BPMN 2.0模式兼容的XML文档,获得关于图形和连接的相关信息并绘制出来。
在这两个库之上,bpmn.js定义了BPMN的细节,比如外观、建模规则和工具(如调色板)。我们将在下面的段落中详细介绍各个组件。
图交互/建模(diagram-js)
diagram-js是一个工具箱,用于在web上显示和修改图表。它允许我们渲染视觉元素并在它们之上构建交互体验。它为我们提供了一个非常简单的模块系统,用于构建特殊的服务和服务的依赖注入。这个系统还提供了一些核心服务,这些服务实现了图的基本内容。
此外,diagram-js为图形元素及其关系定义了一个数据模型。
模块系统
再下一层,diagram-js使用依赖注入(DI)来连接和发现图组件。这个机制是建立在node-di之上的。
在diagram-js上下文中讨论模块时,我们指的是提供命名服务及其实现的单元。从这个意义上说,服务是一个函数或实例,它可以使用其他服务在图的上下文中执行某些操作。
下面显示了一个与生命周期事件挂钩的服务。它通过eventBus注册一个事件来实现,eventBus是另一个著名的服务:
function MyLoggingPlugin(eventBus) {
eventBus.on('element.changed', function(event) {
console.log('element ', event.element, ' changed');
});
}
// 确保依赖项名称在缩小后仍然可用
MyLoggingPlugin.$inject = [ 'eventBus' ];
我们必须使用模块定义唯一名称发布服务:
import CoreModule from 'diagram-js/lib/core';
// 作为一个模块导入
export default {
__depends__: [ CoreModule ], // {2}
__init__: [ 'myLoggingPlugin' ], // {3}
myLoggingPlugin: [ 'type', MyLoggingPlugin ] // {1}
};
该定义告诉DI基础设施,该服务名为myLoggingPlugin {1},它依赖于diagram-js核心模块{2} 并且服务应该在创建关系图{3}时初始化。要了解更多细节,请查看node-di的文档。
我们现在可以通过我们的自定义模块引导diagram-js
要将模块插入bpmn-js,可以使用additionalModules选项,如扩展Modeler部分所示。
核心服务
bpmn-js核心是围绕一些基本服务构建的:
● Canvas -提供了添加和删除图形元素的api;处理元素生命周期,并提供用于缩放和滚动的api。
● EventBus -该库的全局通信通道具有防火和遗忘策略。可以订阅各种事件,并在事件发出后立即采取行动。事件总线帮助我们解耦关注点并模块化功能,以便新特性能够轻松地与现有行为挂钩。
● ElementFactory -根据bpmn-js的内部数据模型创建形状和连接的工厂。
● ElementRegistry—知道添加到图中的所有元素,并提供api来根据id检索元素及其图形表示。
● GraphicsFactory -负责创建图形和连接的图形表示。实际的外观是由渲染器定义的,即绘制模块中的DefaultRenderer。
数据模型
实际上,diagram-js实现了一个由形状和连接组成的简单数据模型。
数据模型要点:形状和连接
一个形状有父节点、子节点列表以及传入和传出连接列表。
一个连接有父节点、源节点和目标节点,指向一个形状。
ElementRegistry负责根据该模型创建形状和连接。
在建模过程中,建模服务将根据用户操作更新元素关系。
辅助服务(即工具箱)
除了数据模型及其核心服务之外,diagram-js还提供了丰富的辅助工具工具箱。
CommandStack-负责建模过程中的重做和撤销。
● ContextPad-提供一个元素的上下文操作。
● Overlays——提供api来附加额外的信息到图元素。
● Modeling——提供用于更新画布上的元素(移动、删除)的api
● Palette
…
让我们继续讨论幕后发生的BPMN魔法。
BPMN元模型(bpmn-moddle)
bpmn-moddle封装了BPMN 2.0元模型,为我们提供了读写BPMN 2.0 XML文档的工具。在导入时,它将XML文档解析为JavaScript对象树。在建模过程中对该树进行编辑和验证,然后在用户希望保存图时将其导出回BPMN 2.0 XML。因为bpmn-moddle封装了有关BPMN的知识,我们能够在导入和建模期间进行验证。根据结果,我们可以约束某些建模操作,并向用户输出有用的错误消息和警告。
就像bpmn-js一样,bpmn-moddle的基础也是建立在两个库之上的:
● moddle提供了一种用JavaScript定义元模型的简洁方法
● 基于moddle读写XML文档的model-xml
从本质上讲,bpmn-moddle将BPMN规范作为元模型添加进来,并为BPMN模式验证提供了一个简单的接口。从库的角度来看,它提供了以下API:
● fromXML -从给定的XML字符串创建BPMN树
● toXML - 向BPMN 2.0 XML编写BPMN对象树
BPMN元模型对于bpmn -js是必不可少的,因为它允许我们验证我们使用的BPMN 2.0文档,提供正确的建模规则,并导出所有遵循BPMN模型的人都能理解的有效BPMN文档。
把东西连接起来(bpmn-js)
我们学过bpms -js是建立在diagram-js和bpms -moddle之上的。它将两者联系在一起,并添加BPMN外观。这包括BPMN调色板、BPMN背景板以及BPMN 2.0特定规则。在本节中,我们将解释它在建模的不同阶段是如何工作的。
当我们导入BPMN 2.0文档时,bpmn-moddle会将其从XML解析为对象树。bpmn-js呈现该树的所有可见元素,即在画布上创建各自的形状和连接。因此,它将BPMN元素和图形元素联系在一起。这将产生一个结构,如下所示,用于初始事件形状。
{
id: 'StartEvent_1',
x: 100,
y: 100,
width: 50,
height: 50,
businessObject: {
$attrs: Object
$parent: {
$attrs: Object
$parent: ModdleElement
$type: 'bpmn:Process'
flowElements: Array[1]
id: 'Process_1'
isExecutable: false
}
$type: 'bpmn:StartEvent'
id: 'StartEvent_1'
}
}
您可以通过businessObject属性从每个图形元素访问底层BPMN类型。
由于BpmnRenderer,bpmn -js也知道每个BPMN元素的样子。通过插入渲染周期,您还可以定义单个BPMN元素的自定义表示。
一旦导入完成,我们就可以开始建模了。我们使用规则来允许或不允许某些建模操作。这些规则由BpmnRules定义。我们将这些规则基于OMG定义的BPMN 2.0标准。然而,正如前面提到的,其他人也可能与规则评估挂钩,以贡献不同的行为。
建模模块捆绑了BPMN 2.0相关的建模功能。它添加了BPMN 2.0特定的建模行为,并负责在用户执行的每个建模操作中更新BPMN 2.0文档树(cf. BpmnUpdater)。请查看它,以更深入地了解规则、行为和BPMN更新周期。
当纯粹从库的角度来看bpmn-js时,值得一提的是它可以有三种形式:
● Viewer展示图
● NavigatedViewer显示和导航BPMN图
● Modeler 建模BPMN图
版本之间的唯一区别是它们捆绑了不同的功能集。NavigatedViewer添加了用于导航画布的模块,Modeler添加了大量用于创建、编辑和与画布上的元素交互的功能。