Matreshka.js 2 — tl;dr
- вторник, 4 апреля 2017 г. в 03:15:17
Краткий обзор фреймворка Matreshka.js. В посте используется ECMAScript 2017, который можно переписать на ECMAScript 5.
Функция bindNode
связывает свойство и элемент.
const object = { name: 'Brendan' };
const node = document.querySelector('.name');
Matreshka.bindNode(object, 'name', node);
object.name = 'Doug';
Если свойство меняется, меняется и элемент, если элемент меняется (например, пользователь вводит текст), меняется и свойство. Из коробки Matreshka.js умеет обрабатывать любые элементы форм. Можно объявить байндинг для произвольных элементов см. документацию.
Функция calc
связывает одно свойство с другими.
Matreshka.calc(object, 'fullName', ['firstName', 'lastName'], (firstName, lastName) => {
return `${firstName} ${lastName}`
});
object.firstName = 'Brendan';
object.lastName = 'Eich';
// ...
console.log(object.fullName); // "Brendan Eich"
Когда меняется свойство-источник (firstName
или lastName
), меняется и свойство-цель (fullName
).
Вместе с функцией bindNode
можно объявлять длинные цепочки зависимостей: свойство a
зависит от состояния элемента e1
, свойство b
зависит от свойства a
, свойство c
зависит от элементов e2
и e3
и от свойства b
, изменение которого, как следствие, меняет e1
, e2
и e3
...
Такие зависимости можно представить как таблицу в табличном процессоре (например, Excel): в каждый момент времени вы думаете об одной формуле, а не о многочисленных связях все ячеек. Как следствие, получаете меньше багов, так как нужно думать об атомарных сущностях, а не обо всей "таблице" (приложении). Больше информации в документации.
Функция on
ловит, а trigger
генерирует события.
Matreshka.on(object, 'something', () => alert('something is happened'));
Matreshka.trigger(object, 'something');
Можно слушать изменения свойств, чтоб вызвать кастомный код. Таким образом в цепочку зависимостей можно добавить, скажем, fetch запрос.
Matreshka.on(object, 'change:fullName', async () => {
await fetch('/api/name', { method: 'post', body: this.fullName });
// ...
})
События в Matreshka.js — это отдельная тема. Можно объявлять делегированные события объекта (слушать изменения в глубине дерева объектов), DOM события, делегированные DOM события и пр. Читайте подробную статью на эту тему.
Функция mediate
добавляет рантайм валидацию и конвертацию значений свойств. Например, можно объявить свойство определенного типа, можно ограничить свойство указанным диапазоном, можно генерировать исключение, если необходимо.
Matreshka.mediate(object, 'x', x => String(x));
object.x = 42;
console.log(object.x, typeof object.x); // "42", "string"
Экземпляры класса Matreshka
содержат те же методы, работающие с объектом, с тем отличием, что не нужно передавать объект первым аргументом: им становится this
.
class User extends Matreshka {
constructor() {
super();
this.bindNode('fullName', '.full-name');
this.calc('fullName', ['firstName', 'lastName'], (firstName, lastName) => {
return `${firstName} ${lastName}`
});
// chained call can be used there: super().bindNode(...).calc(...);
}
}
Этот класс объединяет описанные ниже классы Matreshka.Array
и Matreshka.Object
, объявляя общие для всех классов методы.
Класс Matreshka.Object
отвечает за данные, типа "ключ-значение". Это данные, которые разработчик желает отделить от всех остальных свойств и, скажем, отправить на сервер или сохранить в локальном хранилище. Для этого фреймворку нужно указать на то, какие свойства отвечают за бизнес логику (например, имя пользователя), какие за временное состояние приложения (например, виден ли DOM элемент). За это отвечает метод addDataKeys
.
class User extends Matreshka.Object {
constructor() {
super();
this.firstName = 'Brendan';
this.lastName = 'Eich';
this.language = 'JavaScript';
this.addDataKeys(['firstName', 'lastName']);
console.log(JSON.stringify(this)); // '{"firstName": "Brendan", "lastName": "Eich"}'
}
}
Matreshka.Array
— отвечает за массивоподобные коллекции во фреймворке. Экземпляры, помимо методов унаследованных у Matreshka
, содержит методы из нативного Array
(push
, splice
, map
, reduce
...).
Для коллекций, подобно Backbone.Collection, можно указать "модель" (свойство Model
) с тем отличием, что ею может высупать любой класс.
class Users extends Matreshka.Array {
get Model() { return User; }
constructor() {
super();
this.push({ firstName: 'Brendan', lastName: 'Eich' });
console.log(this[0] instanceof User); // true
}
}
Коллекция может автоматически рендериться при изменени содержимого. Для этого нужно указать куда вставлять новосозданные DOM элементы и как их рендерить. Для указания места рендеринга, нужно привязать свойству sandbox
, либо свойству container
(разницу см. в документации) DOM узел. Для указания того, как рендерить элемент, нужно восмользоваться виртуальным методом itemRenderer
(либо определить renderer
для модели).
class Foo extends Matreshka.Array {
itemRenderer() {
return '<div>Hello, world!</div>';
}
constructor() {
super();
this.bindNode('sandbox', '.sandbox-node');
this.push({}); // inserts <div>Hello, world!</div> to .sandbox-node
}
}
Подробнее о классах в посте "От простого к простому".
Всё указанное выше можно импортировать в качестве CJS/ES2015 модуля:
import calc from 'matreshka/calc';
import MatreshkaArray from 'matreshka/array';
За роутинг отвечает отдельная библиотека matreshka-router. Она связвает части адреса со свойствами:
Matreshka.initRouter(object, '/a/b/c/');
object.a = 'foo';
object.b = 'bar';
object.c = 'baz';
// makes location.hash to be #!/foo/bar/baz/
Подробно описано в статье.
Matreshka.js заполняет пропасть между джуном и сеньором, позволяя на раннем этапе писать модульные и расширяемые приложения. Всё, что требуется — это знание языка и основных понятий.
Всем спасибо.