defi.js — это библиотека, включающая в себя дюжину функций, которые добавляют интересные фичи любым JavaScript объектам с помощью геттеров и сеттеров.
Гифка для привлечения внимания (3.5МБ)
Репозиторий
В качестве Hello World создадим небольшой виджет, состоящий из поля имени, фамилии и приветствия (демо).
<input class="first"> <input class="last"> <output class="greeting"></output>
// данные по умолчанию const obj = { first: 'John', last: 'Doe' }; // слушаем изменения в свойствах first и last // если произошло изменение, сообщим об этом в консоли defi.on(obj, 'change:first', () => console.log('First name is changed')); defi.on(obj, 'change:last', () => console.log('Last name is changed')); // автоматически генерируем приветствие (свойство greeting) каждый раз, // когда first или last изменились defi.calc(obj, 'greeting', ['first', 'last'], (first, last) => `Hello, ${first} ${last}`); // объявляем двусторонний байндинг между свойствами // и соответствующими элементами на странице defi.bindNode(obj, { first: '.first', last: '.last', greeting: '.greeting' });
В итоге, если first или last изменились, обработчики события сообщают об этом в консоли, свойство greeting автоматически обновляется, а его элемент получает новое значение (по умолчанию, "Hello, John Doe"). Это случается каждый раз, когда свойства меняются, причем не имеет значения, каким образом. Можно установить значение с помощью кода obj.first = 'Jane', либо изменив значение поля, и все остальные изменения произойдут автоматически.
В случае, если есть необходимость синхронизации свойства объекта и innerHTML произвольного HTML элемента (в примере мы использовали тэг output), в функцию bindNode нужно передать так называемый "байндер", который отвечает за то, как синхронизировать состояние элемента и свойства объекта. По умолчанию bindNode не знает, как работать с нодами, не являющимися элементами форм.
const htmlBinder = { setValue: (value, binding) => binding.node.innerHTML = value, }; // изменение свойства obj.greeting обновит innerHTML любого элемента defi.bindNode(obj, 'greeting', '.greeting', htmlBinder)
Кроме этого, можно воспользоваться html из библиотеки common-binders (это коллекция байндеров общего назначения).
const { html } = require('common-binders'); // изменение свойства obj.greeting обновит innerHTML любого элемента defi.bindNode(obj, 'greeting', '.greeting', html())
Методы API
Подробную документацию ко всем методам, доступные вариации вызова, флаги и др. можно прочесть на defi.js.org. Стоит упомянуть, что кроме методов ниже, у defi.js есть библиотека для роутинга: defi-router.
- bindNode — Связывает свойство объекта и DOM узел для двустороннего байндинга.
// обычное использование // (для стандартных HTML5 элементов форм, см. defaultBunders) defi.bindNode(obj, 'myKey', '.my-element'); // кастомный байндинг defi.bindNode(obj, 'myKey', '.my-element', { // событие, которое говорит об изменении элемента // (можно использовать функцию для не-DOM событий) on: 'click', // как извлечь текущее состояние элемента? getValue: ({ node }) => someLibraryGetValue(node), // как установить состояние элемента при изменении свойства? setValue: (v, { node }) => someLibrarySetValue(node, v), // как инициализировать элемент (библиотеку или виджет)? // это можно сделать любым способом // но 'initialize' добавляет немного синтаксического сахара initialize: ({ node }) => someLibraryInit(node), }); obj.myKey = 'some value'; // обновит элемент
- calc — Создает зависимость одного свойства объекта от других свойств (в том числе, из других объектов).
defi.calc(obj, 'a', ['b', 'c'], (b, c) => b + c); obj.b = 1; obj.c = 2; console.log(obj.a); // 3
- mediate — Модифицирует значение свойства при его изменении.
defi.mediate(obj, 'x', value => String(value)); obj.x = 1; console.log(obj.x); // "1" console.log(typeof obj.x); // "string"
- on — Добавляет обработчик событий. На сайте с документацией есть небольшая статья, описывающая все возможные события.
defi.on(obj, 'change:x', () => { alert(`obj.x now equals ${obj.x}`); }); obj.x = 1;
- off — Удаляет обработчик события.
defi.off(obj, 'change:x bind');
- trigger — Триггерит событие.
defi.on(obj, 'foo bar', (a, b, c) => { alert(a + b + c); }); defi.trigger(obj, 'foo', 1, 2, 3); // вызывает alert(6)
- unbindNode — Отключает связывание элемента и DOM узла.
defi.bindNode(obj, 'myKey', '.my-element'); defi.unbindNode(obj, 'myKey', '.my-element');
- bound — Возвращает DOM элемент, связанный с заданным свойством.
defi.bindNode(obj, 'myKey', '.my-element'); const node = defi.bound(obj, 'myKey'); // вернет document.querySelector('.my-element')
- chain — Используется для цепочечного вызова функций defi.js.
defi.chain(obj) .calc('a', 'b', b => b * 2) .set('b', 3) .bindNode('c', '.node');
- defaultBinders — Массив функций, возвращающих соответствующий байндер или undefined. Позволяет
bindNodeсвязывать кастомные элементы и свойства без явного указания байндера.
defi.defaultBinders.unshift(element => { // если у элемента есть класс "foo" if(element.classList.contains('foo')) { // значит, нужно использовать этот байндер return { on: ..., getValue: ..., setValue: ... }; } }); // ... defi.bindNode(obj, 'myKey', '.foo.bar');
- lookForBinder — Возвращает байндер, соответствующий элементу, если таковой вернула одна из функций
defaultBinders.
const element = document.createElement('input'); element.type = 'text'; console.log(defi.lookForBinder(element));
- remove — Удаляет свойство объекта и соотвествующие обработчики событий.
defi.remove(obj, 'myKey');
- set — Устанавливает свойство объекта, как и сеттер, но дает возможность передать какие-нибудь данные в обработчик события
change:KEY. Так же можно сделать установку значения свойства "тихим", т. е. не вызыватьchange:KEYвовсе.
defi.set(obj, 'myKey', 3, { silent: true });
defi.js — это переработанный и упрощенный хард-форк фреймворка Matreshka.js, который включал в себя рендеринг массивов, несколько классов и больше методов. Некоторые методы, которые потенциально могли бы попасть в defi.js были заменены на опции к другим методам, например, вместо once и onDebounce можно использовать метод on, передав опции once: true или debounce: number.
Спасибо, что прочли до конца. Всем хорошего дня.
