如何让bpmn-js暴露可选模块?模块化建模器设计问询
Great question! I’ve tackled similar modularity challenges with bpmn-js before, so let me break down what works (and what to watch out for):
First, Context on bpmn-js’s Module System
bpmn-js is built on top of diagram-js’s dependency injection (DI) system, which is designed to initialize modules once at instantiation time rather than support dynamic runtime changes. There’s no official, exposed modules(), addModule(), or removeModule() API—this is intentional, as the core library prioritizes stability over dynamic module swapping.
Workarounds & Stable Solutions
1. Predefined Role-Based Module Arrays (Most Reliable)
The official recommended approach is to define module arrays tailored to each user role upfront, then instantiate a Modeler with the appropriate set. This avoids versioning risks and aligns with bpmn-js’s design:
import BpmnModeler from 'bpmn-js/lib/Modeler'; // Define module sets for different roles const adminModules = [ // Base modeling modules ...BpmnModeler.prototype._modelingModules, // Add admin-specific features require('bpmn-js/lib/features/label-editing'), require('bpmn-js/lib/features/keyboard'), require('bpmn-js/lib/features/comment') ]; const viewerModules = [ // Base interaction modules ...BpmnModeler.prototype._interactionModules, // Add viewer-only features require('bpmn-js/lib/features/zoomscroll'), require('bpmn-js/lib/navigation/movecanvas') ]; // Create role-specific Modeler instances const adminModeler = new BpmnModeler({ modules: adminModules }); const viewerModeler = new BpmnModeler({ modules: viewerModules });
This relies on accessing the prototype’s module groups (_modelingModules, _interactionModules, _coreModules), which are internal but rarely change across bpmn-js versions—far more stable than traversing node_modules directories.
2. Custom Wrapper Class for Dynamic Management
If you truly need runtime module changes, you can wrap the Modeler to manage a module list and interact with the DI container directly. Note: This has limitations (already initialized components won’t be reloaded), but it works for adding new services/features:
import BpmnModeler from 'bpmn-js/lib/Modeler'; class ModularBpmnModeler extends BpmnModeler { constructor(options = {}) { // Combine base modules with user-provided ones this._managedModules = [ ...BpmnModeler.prototype._coreModules, ...BpmnModeler.prototype._interactionModules, ...BpmnModeler.prototype._modelingModules, ...(options.modules || []) ]; super({ ...options, modules: this._managedModules }); } // List all modules (use custom __name property or toString for identification) modules() { return this._managedModules.map(module => module.__name || module.toString().slice(0, 50) // Truncate for readability ); } // Add a module at runtime addModule(module) { const exists = this._managedModules.some(m => m === module); if (exists) return; this._managedModules.push(module); // Register the module with the DI injector if possible const injector = this.get('injector'); if (injector) { if (typeof module === 'function') { module(injector); } else if (typeof module === 'object') { Object.keys(module).forEach(key => injector.register(key, module[key])); } } } // Remove a module (note: doesn't destroy existing instances) removeModule(moduleIdentifier) { this._managedModules = this._managedModules.filter(module => { const id = module.__name || module.toString().slice(0, 50); return id !== moduleIdentifier; }); } } // Usage example const modeler = new ModularBpmnModeler(); modeler.addModule(require('bpmn-js/lib/features/export')); console.log(modeler.modules());
Key Caveats for Dynamic Management
- DI Container Limitation: The DI container is built once at instantiation. Adding a module after initialization will register new services, but existing components (like UI elements) won’t automatically update or reload.
- Module Identification: Since modules are functions/objects, you’ll need to add custom identifiers (like a
__nameproperty) to reliably track and remove them.
Are There Hidden Official APIs?
No—bpmn-js and diagram-js don’t expose any hidden module management methods. The prototype’s module groups (_modelingModules, etc.) are the closest thing to a stable "module catalog" you’ll find, and they’re safe to use as long as you test across minor version updates.
内容的提问来源于stack exchange,提问作者Jankapunkt




