Поточна версія фреймворку містить численні критичні зміни порівняно з її попередницею.
Давайте почнемо з вивчення відмінностей між 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 | на ваш розсуд | |
Плагін з'єднань | відповідає як за рендеринг, так і за взаємодію | відповідає лише за взаємодію |
Підключіть плагін, імпортувавши його за замовчуванням.
Другий параметр використовується для передачі опцій/параметрів плагіна:
// v1
import HistoryPlugin from 'rete-history-plugin';
editor.use(HistoryPlugin, { keyboard: true });
Усі плагіни реалізовані як класи та можуть бути розширені, забезпечуючи гнучку кастомізацію без зміни ядра.
// 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 вузли генеруються за допомогою компонентів, зареєстрованих у редакторі, що дозволяє створювати численні екземпляри вузлів, що належать до одного типу компонентів.
// 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 });
Поточна версія не включає компонент як абстракцію, але ви можете реалізувати подібний підхід, якщо потрібно.
// 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 форматом, оскільки їх можна використовувати для імпорту/експорту.
// v1
node.putData('myData', 'data')
control.putData('myData', 'data') // де контрол є частиною вузла
У поточній версії немає жорстких інструкцій щодо імпорту/експорту, що означає, що ви маєте повну гнучкість у тому, як зберігати свої дані у вузлах.
// v2
class MyNode extends ClassicPreset.Node {
myData = 'data'
}
Через обмеження, згадані раніше, редактор можна легко експортувати та імпортувати.
// v1
const data = editor.toJSON();
await editor.fromJSON(data);
Поточна версія містить переглянутий підхід, який вимагає імплементації, як показано в Імпорт/експорт.
Вибір елементів є функцією, вбудованою в редактор
// v1
editor.selected.list
editor.selected.add(node, accumulate)
Недоліком цієї реалізації є її нездатність підтримувати щось, крім вибору вузла.
Вибір вузлів (та інших елементів) виглядає так:
// 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)
Типовий спосіб прослуховування подій, які можна попередити
// v1
editor.on('nodecreate', node => {
return node.canCreate
});
* - незміння
** - переміщені до іншого пакету
*** - видалено
rete
rete-connection-plugin
У поточній версії використовується особливий тип реалізації сигналу, який включає сигнали як об’єкти. Крім того, пайпи використовуються або для маніпулювання цими об’єктами, або для запобігання поширенню сигналу.
// 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
Існує вбудована валідація з'єднань на основі сумісності сокетів
// v1
const anyTypeSocket = new Rete.Socket('Any type');
numSocket.combineWith(anyTypeSocket);
Цей підхід простий, але має деякі обмеження.
Валідацію з'єднань можна реалізувати незалежно, що забезпечує більшу гнучкість
// v2
editor.addPipe(context => {
if (context.type === 'connectioncreate') {
if (canCreateConnection(context.data)) return false
}
return context
})
Компонент із визначеним worker
методом має бути зареєстрований
// v1
const engine = new Rete.Engine('[email protected]');
engine.register(myComponent);
Визначте метод worker
компонента
// v1
worker(node, inputs, outputs){
outputs['num'] = node.data.num;
}
Ініціювати обробку
// v1
await engine.process(data);
Створіть екземпляр DataflowEngine
, щоб підключитися його до редактора. На відміну від першої версії, немає необхідності передавати data
з вузлами та підключеннями.
// v2
import { DataflowEngine } from 'rete-engine'
const engine = new DataflowEngine<Schemes>()
editor.use(engine)
Приклад методу вузла
// v2
data(inputs) {
const { left, right } = inputs
return { sum: left[0] + right[0] }
}
Розпочати обробку
// v2
engine.fetch(node.id)
Цей підхід реалізовано за допомогою rete-task-plugin
і на основі Rete.Engine
. Тому він має вищезгадані обмеження
// v1
import TaskPlugin from 'rete-task-plugin';
editor.use(TaskPlugin);
Конструктор компонента має визначення виходів, призначених для потоку керування або потоку даних
// v1
this.task = {
outputs: { exec: 'option', data: 'output' },
init(task) {
task.run('any data');
task.reset();
}
}
Визначте метод worker
, який повертає дані та визначає закриті порти виводу для потоку керування
// v1
worker(node, inputs, data) {
this.closed = ['exec'];
return { data }
}
Використовується пакет rete-engine
, який має окрему реалізацію двигуна для керування потоком
// v2
import { ControlFlowEngine } from 'rete-engine'
const engine = new ControlFlowEngine<Schemes>()
editor.use(engine)
За замовчуванням усі порти налаштовані на передачу контролю, але ви можете призначити тільки певні порти для цього
// v2
const engine = new ControlFlowEngine<Schemes>(() => {
return {
inputs: () => ["exec"],
outputs: () => ["exec"]
};
});
Методом вузла служить наступне:
// v2
execute(input: 'exec', forward: (output: 'exec') => void) {
forward('exec')
}
На відміну від попередньої версії, цей підхід повністю відокремлений від потоку даних. Тим не менш, його можна використовувати в поєднанні з DataflowEngine
.
// v2
async execute(input: 'exec', forward: (output: 'exec') => void) {
const inputs = await dataflow.fetchInputs(this.id)
forward('exec')
}
Для демонстрації ми вирішили використати rete-react-render-plugin
// v1
import ReactRenderPlugin from 'rete-react-render-plugin';
editor.use(ReactRenderPlugin)
// v2
import { ReactPlugin } from 'rete-react-plugin'
const reactPlugin = new ReactPlugin<Schemes, AreaExtra>()
area.use(reactPlugin)
Наступний код використовується для визначення компонентів, необхідних для певних вузлів і контролів
// 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 };
}
}
Крім того, компонент можна вказати для всіх вузлів
// v1
editor.use(ReactRenderPlugin, { component: MyNode });
У цій версії компоненти, які потрібно візуалізувати, визначені в класичному пресеті, який підключений
// v2
reactPlugin.addPreset(ReactPresets.classic.setup({ customize: {
node(data) {
return MyNode
},
control() {
return MyReactControl
}
}}))
Цей підхід забезпечує більшу гнучкість, дозволяючи визначати додаткові умови в обробниках
Отримайте вид вузла та виконайте його метод translate
// v1
editor.view.nodes.get(node).translate(x, y)
Екземпляр плагіна містить метод translate
, якому потрібен лише ідентифікатор вузла.
// v2
await area.translate(node.id, { x, y })
Плагін пропонує підхід до позиціонування вузлів, але його функціональність значно обмежена.
// v1
import AutoArrangePlugin from 'rete-auto-arrange-plugin';
editor.use(AutoArrangePlugin, {});
editor.trigger('arrange');
Плагін використовує розширену функціональність пакета elkjs
.
// 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
потрібен екземпляр редактора, який відповідає за візуалізацію
// v1
import AreaPlugin from "rete-area-plugin";
AreaPlugin.zoomAt(editor);
Для цілей візуалізації в цій версії потрібен екземпляр AreaPlugin
.
// v2
import { AreaExtensions } from "rete-area-plugin";
AreaExtensions.zoomAt(area, editor.getNodes());