为了扩展BPMN.JS的功能,我们通常在Modeler/Viewer实例化时通过传入additionalModules参数的方法来扩展/修改BPMN-JS的功能。

一张图搞懂BPMN-JS设计

BPMN-JS通常通过类似下面的代码来自定义DI(依赖注入)模块:

export default class CustomRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, HIGH_PRIORITY);

    this.bpmnRenderer = bpmnRenderer;
  }
  canRender(element) {
    return false;
  }
  drawShape(parentNode, element) {
    ...
  }
  getShapePath(shape) {
    ...
    return shape
  }
}
CustomRenderer.$inject = ['eventBus', 'bpmnRenderer'];

export default {
  __init__: ['CustomRenderer'],
  CustomRenderer: ['type', CustomRenderer]
}

上面代码实现了一个继承自BaseRendererCustomRenderer类,BaseRenderer类是diagram-js包中实现的一个渲染基类(抽象类),里面定义了一些空的原型方法。通过上面的写法,我们就可以覆盖掉bpmn-js中BaseRenderer类的实现,从而实现了bpmn-js自定义渲染功能。

另外上面代码CustomRenderer.$inject = ['eventBus', 'bpmnRenderer']; 而这实际上是bpmn.js依赖的diagram-js包所依赖的didi实现的依赖注入。

didi是用JavaScript实现的一个依赖注入/控制反转容器。该库对外暴露了如下4个接口

  • annotate
  • parseAnnotations
  • Module
  • Injector

annotate

该方法用来将形如['a', 'b', 'c', function(a, b, c) {}]通用的依赖注入写法,转换为didi特定的语法:

import { annotate } from "didi";

var fn = annotate(['a', 'b', 'c', function(a, b, c) {}]);
// 或
var fn = annotate('a', 'b', 'c', function(a, b, c) {});

// 输出结果为
var fn = function(a, b, c) {};
fn.$inject = ['a', 'b', 'c'];

parseAnnotations

该方法用来解析JavaScript函数的形参到一个数组,示例:

function Human(name, age) {
  this.name = name;
  this.age = age;
}

var res = parseAnnotations(Human);
// 结果 ['name', 'age']

Module

这是一个类,didi提供的自定义module接口,示例:

var { Module } = require('didi');

var carModule = new Module();
carModule.type('car', Car).factory('engine', createPetrolEngine).value('power', 1184);

// 或者通过对象字面量的方式来定义didi要注册的module
var carModule = {
  'car': ['type', Car],
  'engine': ['factory', createPetrolEngine],
  'power': ['value', 1184]
};

// 注册到Injector
var injector = new Injector([module]);

Injector

注入器,可以将上面定义的模块,通过一个数组参数实例化进该容器

const injector = new Injector([
  carModule
])

// get
injector.get('car').start();

// invoke
injector.invoke(['car', function(car) {
  car.start();
}]);

// 根据类的形参,在locals中查找依赖,并在类实例化时将形参依赖进行注入
injector.instantiate(function(a, b, c) {}, locals);
//
injector.createChild(modules, forceNewInstances);