Поточна версія фреймворку містить численні критичні зміни порівняно з її попередницею.
Давайте почнемо з вивчення відмінностей між v1 і v2 як з точки зору розробника, так і з точки зору користувача:
Контекст | v1 | v2 | Посилання |
---|---|---|---|
TypeScript | Часткова підтримка | TypeScript-first | |
Швидкий старт | Codepen приклади | DevKit, Codesandbox приклади | |
Архітектура | На основі подій | Middleware-подібні сигнали | |
Інструменти | rete-cli | rete-cli , rete-kit , rete-qa | |
Тестування | модульне тестування | блок + E2E тестування | |
UI | |||
Порядок вузлів | фіксований порядок | висувати вперед обрані вузли | |
Вибір | вбудований лише для вузлів | розширений вибір + спеціальні елементи | |
Контроли | немає вбудованих елементів керування | вбудований класичний контроль входу | |
Упорядкування вузлів | обмежений | на основі elkjs | |
Код | |||
Створення вузла | Компонентний підхід | на ваш розсуд | |
Editor/Engine ідентифікатори | обов'язковий, необхідний для імпорту/експорту | на ваш розсуд | |
Ідентифікатор вузла | інкрементальний десятковий | унікальний | |
Імпорт/експорт | Вбудований, обмежений | на ваш розсуд | |
Валідація | Перевірка на основі сокетів | на ваш розсуд | |
Dataflow обробка | обмежено (без рекурсії) | DataflowEngine з динамічною вибіркою | |
Control flow обробка | симулюється плагіном Task з обмеженнями | ControlFlowEngine | |
Модулі | rete-module-plugin | на ваш розсуд | |
Плагін з'єднань | відповідає як за рендеринг, так і за взаємодію | відповідає лише за взаємодію |
Підключіть плагін, імпортувавши його за замовчуванням.
Другий параметр використовується для передачі опцій/параметрів плагіна:
ts// v1 import HistoryPlugin from 'rete-history-plugin'; editor.use(HistoryPlugin, { keyboard: true });
Усі плагіни реалізовані як класи та можуть бути розширені, забезпечуючи гнучку кастомізацію без зміни ядра.
ts// v2 import { HistoryPlugin, HistoryExtensions, Presets } from 'rete-history-plugin' const history = new HistoryPlugin<Schemes>() history.addPreset(Presets.classic.setup()) HistoryExtensions.keyboard(history) area.use(history)
У версії v1 вузли генеруються за допомогою компонентів, зареєстрованих у редакторі, що дозволяє створювати численні екземпляри вузлів, що належать до одного типу компонентів.
ts// v1 class NumComponent extends Rete.Component { constructor(){ super("Number"); } builder(node) { node.addControl(new NumControl('num')) node.addOutput(new Rete.Output('num', "Number", numSocket)) return node } } const numComponent = new NumComponent() editor.register(numComponent); const node = await numComponent.createNode({ num: 2 });
Поточна версія не включає компонент як абстракцію, але ви можете реалізувати подібний підхід, якщо потрібно.
ts// v2 const node = new ClassicPreset.Node('Number') node.addControl('num', new NumControl('num')) node.addOutput('num', new ClassicPreset.Output(numSocket, "Number")); await editor.addNode(node)
Дані можна зберегти за допомогою методу putData
. Очікується, що дані мають бути дійсним JSON форматом, оскільки їх можна використовувати для імпорту/експорту.
ts// v1 node.putData('myData', 'data') control.putData('myData', 'data') // де контрол є частиною вузла
У поточній версії немає жорстких інструкцій щодо імпорту/експорту, що означає, що ви маєте повну гнучкість у тому, як зберігати свої дані у вузлах.
ts// v2 class MyNode extends ClassicPreset.Node { myData = 'data' }
Через обмеження, згадані раніше, редактор можна легко експортувати та імпортувати.
ts// v1 const data = editor.toJSON(); await editor.fromJSON(data);
Поточна версія містить переглянутий підхід, який вимагає імплементації, як показано в Імпорт/експорт.
Вибір елементів є функцією, вбудованою в редактор
ts// v1 editor.selected.list editor.selected.add(node, accumulate)
Недоліком цієї реалізації є її нездатність підтримувати щось, крім вибору вузла.
Вибір вузлів (та інших елементів) виглядає так:
ts// v2 const selector = AreaExtensions.selector() const accumulating = AreaExtensions.accumulateOnCtrl() const nodeSelector = AreaExtensions.selectableNodes(area, selector, { accumulating }); editor.getNodes().filter(node => node.selected) nodeSelector.select(add.id)
Типовий спосіб прослуховування подій, які можна попередити
ts// v1 editor.on('nodecreate', node => { return node.canCreate });
* - незміння
** - переміщені до іншого пакету
*** - видалено
rete
rete-connection-plugin
У поточній версії використовується особливий тип реалізації сигналу, який включає сигнали як об’єкти. Крім того, пайпи використовуються або для маніпулювання цими об’єктами, або для запобігання поширенню сигналу.
ts// v2 editor.addPipe(context => { if (context.type === 'nodecreate') return return context })
rete
rete-area-plugin
rete-connection-plugin
rete-angular-plugin
rete-vue-plugin
rete-react-plugin
Існує вбудована валідація з'єднань на основі сумісності сокетів
ts// v1 const anyTypeSocket = new Rete.Socket('Any type'); numSocket.combineWith(anyTypeSocket);
Цей підхід простий, але має деякі обмеження.
Валідацію з'єднань можна реалізувати незалежно, що забезпечує більшу гнучкість
ts// v2 editor.addPipe(context => { if (context.type === 'connectioncreate') { if (canCreateConnection(context.data)) return false } return context })
Компонент із визначеним worker
методом має бути зареєстрований
ts// v1 const engine = new Rete.Engine('[email protected]'); engine.register(myComponent);
Визначте метод worker
компонента
ts// v1 worker(node, inputs, outputs){ outputs['num'] = node.data.num; }
Ініціювати обробку
ts// v1 await engine.process(data);
Створіть екземпляр DataflowEngine
, щоб підключитися його до редактора. На відміну від першої версії, немає необхідності передавати data
з вузлами та підключеннями.
ts// v2 import { DataflowEngine } from 'rete-engine' const engine = new DataflowEngine<Schemes>() editor.use(engine)
Приклад методу вузла
ts// v2 data(inputs) { const { left, right } = inputs return { sum: left[0] + right[0] } }
Розпочати обробку
ts// v2 engine.fetch(node.id)
Цей підхід реалізовано за допомогою rete-task-plugin
і на основі Rete.Engine
. Тому він має вищезгадані обмеження
ts// v1 import TaskPlugin from 'rete-task-plugin'; editor.use(TaskPlugin);
Конструктор компонента має визначення виходів, призначених для потоку керування або потоку даних
ts// v1 this.task = { outputs: { exec: 'option', data: 'output' }, init(task) { task.run('any data'); task.reset(); } }
Визначте метод worker
, який повертає дані та визначає закриті порти виводу для потоку керування
ts// v1 worker(node, inputs, data) { this.closed = ['exec']; return { data } }
Використовується пакет rete-engine
, який має окрему реалізацію двигуна для керування потоком
ts// v2 import { ControlFlowEngine } from 'rete-engine' const engine = new ControlFlowEngine<Schemes>() editor.use(engine)
За замовчуванням усі порти налаштовані на передачу контролю, але ви можете призначити тільки певні порти для цього
ts// v2 const engine = new ControlFlowEngine<Schemes>(() => { return { inputs: () => ["exec"], outputs: () => ["exec"] }; });
Методом вузла служить наступне:
ts// v2 execute(input: 'exec', forward: (output: 'exec') => void) { forward('exec') }
На відміну від попередньої версії, цей підхід повністю відокремлений від потоку даних. Тим не менш, його можна використовувати в поєднанні з DataflowEngine
.
ts// v2 async execute(input: 'exec', forward: (output: 'exec') => void) { const inputs = await dataflow.fetchInputs(this.id) forward('exec') }
Для демонстрації ми вирішили використати rete-react-render-plugin
ts// v1 import ReactRenderPlugin from 'rete-react-render-plugin'; editor.use(ReactRenderPlugin)
ts// v2 import { ReactPlugin } from 'rete-react-plugin' const reactPlugin = new ReactPlugin<Schemes, AreaExtra>() area.use(reactPlugin)
Наступний код використовується для визначення компонентів, необхідних для певних вузлів і контролів
ts// v1 class AddComponent extends Rete.Component { constructor() { super("Add"); this.data.component = MyNode; } } class MyControl extends Rete.Control { constructor(emitter, key, name) { super(key); this.render = 'react'; this.component = MyReactControl; this.props = { emitter, name }; } }
Крім того, компонент можна вказати для всіх вузлів
ts// v1 editor.use(ReactRenderPlugin, { component: MyNode });
У цій версії компоненти, які потрібно візуалізувати, визначені в класичному пресеті, який підключений
ts// v2 reactPlugin.addPreset(ReactPresets.classic.setup({ customize: { node(data) { return MyNode }, control() { return MyReactControl } }}))
Цей підхід забезпечує більшу гнучкість, дозволяючи визначати додаткові умови в обробниках
Отримайте вид вузла та виконайте його метод translate
ts// v1 editor.view.nodes.get(node).translate(x, y)
Екземпляр плагіна містить метод translate
, якому потрібен лише ідентифікатор вузла.
ts// v2 await area.translate(node.id, { x, y })
Плагін пропонує підхід до позиціонування вузлів, але його функціональність значно обмежена.
ts// v1 import AutoArrangePlugin from 'rete-auto-arrange-plugin'; editor.use(AutoArrangePlugin, {}); editor.trigger('arrange');
Плагін використовує розширену функціональність пакета elkjs
.
ts// v2 import { AutoArrangePlugin, Presets as ArrangePresets } from "rete-auto-arrange-plugin"; const arrange = new AutoArrangePlugin<Schemes>(); arrange.addPreset(ArrangePresets.classic.setup()); area.use(arrange); await arrange.layout()
Для методу zoomAt
потрібен екземпляр редактора, який відповідає за візуалізацію
ts// v1 import AreaPlugin from "rete-area-plugin"; AreaPlugin.zoomAt(editor);
Для цілей візуалізації в цій версії потрібен екземпляр AreaPlugin
.
ts// v2 import { AreaExtensions } from "rete-area-plugin"; AreaExtensions.zoomAt(area, editor.getNodes());