javascript

Matreshka.js 2 — tl;dr

  • вторник, 4 апреля 2017 г. в 03:15:17
https://habrahabr.ru/company/matreshka/blog/325480/
  • Разработка веб-сайтов
  • JavaScript
  • Блог компании Matreshka.js


image



Краткий обзор фреймворка 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.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.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 заполняет пропасть между джуном и сеньором, позволяя на раннем этапе писать модульные и расширяемые приложения. Всё, что требуется — это знание языка и основных понятий.

Всем спасибо.