image
Репозиторий на Github

Всем привет! Спешу сообщить радостную новость. Наконец, вышла первая (1.0) версия фреймворка Матрешка. В этом посте я расскажу о самых важных изменениях во фреймфорке, о новом сайте и русскоязычной документации.

Напомню, Матрешка — JavaScript фреймворк для создания одностраничных приложений, соблюдающий несколько важных принципов:
  • Никакой логики в HTML
  • Минимум сущностей
  • Произвольная архитектура

Матрешка реализует простой синтаксис двустороннего связывания данных и активно использует акцессоры (геттеры и сеттеры).

this.bindNode( 'x', 'input.my-node' );
this.on( 'change:x', function() {
	alert( this.x );
});
this.x = 'Wow!';


В первую очередь, о важных изменениях во фреймворке


Последняя волна переименований. Самое важное из них: bindElement из-за длинного имени (11 символов) был переименован в bindNode (8 символов).

Поддержка всех без исключения полей ввода, включая элементы из спецификации HTML5. bindNode больше не нуждается в третьем аргументе, в случае, если программист желает связать значение свойства объекта с каким-нибудь <input type="datetime-local" class="my-input">.

this.bindNode( 'x', '.my-input' );

Поддержка цикла for..of для Matreshka.Array и Matreshka.Object. Если ваше приложение не нуждается в поддержке Internet Explorer либо если вы используете транспилер (например, Babel), можно смело пробовать новый, крутой синтаксис циклов из ECMAScript 6.

for(let item of this) {
 ...
}

__this__ переименован в sandbox. Теперь ключ, отвечающий за привязку песочницы, так и называется “песочница”.

this.bindNode( 'sandbox', '.app' );

Появился новый кастомный селектор ‘:sandbox’. Раньше нужно было использовать не самый удобный синтаксис, если требовалось выбрать песочницу или элемент внутри песочницы.

this.bindNode( 'x', this.select( '.my-x-node' ) );

Теперь это можно сделать намного элегантнее:

this.bindNode( 'x', ':sandbox .my-x-node' );

Появился новый селектор ‘:bound(KEY)’. В приложениях часто нужно привязывать элементы внутри других привязанных элементов, дабы не дублировать селекторы. Вот так это делалось раньше:

// Vanilla
this.bindNode( 'y', this.bound( 'x' ).querySelector( '.my-y-node' ) );

Или так:

// jQuery
this.bindNode( 'y', this.$bound( 'x' ).find( '.my-y-node' ) );

Теперь то же самое можно сделать так:

this.bindNode( 'y', ':bound(x) .my-y-node' );

Полное совпадение методов Matreshka.Array со встроенными методами Array. По многочисленным просьбам push и unshift теперь возвращают длину массива, вместо ссылки на массив, как и в оригинальном Array.prototype. Единственный метод, который до сих пор отличается от встроенного — это forEach. Он возвращает “себя” вместо undefined.

Новые методы Matreshka.Array. При разработке приложений на базе Матрешки иногда требуется передать какие-нибудь данные в обработчик события изменения массива.

Так как все методы встроенного Array работают аналогично встроенным, у нас нет возможности передать еще один аргумент при вызове этих методов.

// каждый аргумент метода вставляется в массив
this.push( 1, 2, 3 );

Теперь каждый метод, изменяющий массив и позаимствованный из оригинального Array.prototype имеет двойника с именем МЕТОД_. Нижнее подчеркивание указывает на то, что последним аргументом являются произвольные данные для обработчика события или служебные флаги.

this.on( 'modify', function( evt ) {
	console.log( evt.customData );
});

this.push_( 1, 2, 3, {
	customData: 42
});

Если требуется вставить элемент, не вызывая обработчик, нужно передать специальный флаг silent: true в объект события.

this.push_( 1, 2, 3, {
	silent: true
});

Как следствие, методы silentPush, silentPop, silentSplice… были удалены из фреймворка из-за отсутствия в них необходимости.

Поддержка Babel. После исправления единственного бага, связанного со строгим режимом, Матрешка стала доступна для использования вместе с компилятором Babel. Кроме общих синтаксических сладостей, Матрешка поддерживает циклы for..of, о которых писалось выше, и наследование с помощью классов из ECMAScript 6 (благодаря прототипному наследованию, используемого Матрешкой с первого коммита на Github), вместо наследования функцией Class, которая входит в комплект Матрешки.

Обратите внимание. Если приложение нуждается в поддержке Internet Explorer 8, использование функции Class обязательно. Функция использует специальные хаки, которые включают геттеры и сеттеры для объектов-экземпляров Матрешки.

Как видите, было решено не отказываться от поддержки IE8. Код, отвечающий за работу фреймворка в восьмом “осле” не требует много места и слабо влияет на производительность.

Об остальных изменениях можно узнать на сайте.

Обновленная документация


Как показывает практика, камнем преткновения для любой библиотеки или фреймворка является документация. Скажем честно, документация, на момент выходя нулевых версий, была далеко не идеальной. Вещи, типа itemRenderer, не были описаны вовсе (был приведен малопонятный пример и более ничего).

Теперь в документации описаны все без исключения свойства и методы, а текст, кроме английского, доступен и на на русском языке.

С чего начинать?


Пытаясь изучить новый инструмент, включающий в себя много деталей, часто не понимаешь, с чего начать своё обучение. Этим грешит большинство фреймворков: огромное количество свойств, методов, функций, директив… может расстроить или вовсе отпугнуть новичка.

Эта проблема была решена очень просто: самые важные методы и свойства помечены красным флажком image. Те свойства и методы, которые не имеют такого флажка — необязятельны к изучению. Например, крутой метод linkProps, добавляющий зависимость одного свойства от других, можно вовсе не использовать, ограничившись прописанным вручную обработчиком события.

Адаптивная верстка и чтение оффлайн


image

Документацию теперь удобно использовать не только с настольных компьютеров и ноутбуков, но и с мобильных телефонов и планшетов. Сайт работает оффлайн, благодаря использованию HTML5 cache. Эта страница теперь может стать полезным чтивом, в том числе, в дороге или при отсутствии интернета.

Репорт об опечатках


image

Сообщить об опечатке или ошибке можно было и раньше, но теперь у окна опечатки появился дизайн :) Можно выбрать сомнительный текст на сайте и нажать Ctrl+Enter. После этого откроется модальное окно с формой.
Подробнее о том, как сделать окно опечаток и ошибок на сайте, используя Google Forms, можно прочесть здесь.

Комментарии прямо в документации


image

У каждого элемента документации (класс, метод, свойство) есть кнопка “задать вопрос”, которая вызывает Muut Comments (почему, расскажу ниже). Вам не нужно искать соответствующий форум или создавать тикет на Github, можно задать вопрос по части документации прямо из самой документации.

В первых двух версиях использовался Disqus, а, затем, Facebook Comments. Обе системы, к сожалению, не оправдали возложенных на них ожиданий.

Disqus постоянно путает идентификаторы и адреса страниц, комментарии появляются не там, где нужно. Админпанель не позволяет удалять обсуждения, а изменение ссылок вручную не помогает. Это старая проблема Disquss, которую решить пока нельзя (разработчики рекомендуют убедиться в правильности объявленных переменных, которые часто просто-напросто игнорируются системой). Причина, скорее всего, кроется в том, что обсуждения находятся на одной странице, а Disquss с этим испытывает огромные проблемы. Как следствие, было решено перейти на другое бесплатное решение, от которого тоже пришлось отказаться.

Facebook Comments, на первый взгляд, показался неплохим решением задачи. Разработчик — многомиллиардная компания; многие зарегистрированы в этой соцсети; не нужно логиниться, чтобы написать комментарий; идентификаторы не путаются… Проблема одна: уведомления. Facebook Comments не умеет сообщать о новых комментариях модераторам (!). Для обхода был написан костыль, использующий Google Forms в качестве системы уведомлений. Но это полбеды. Самое неприятное — уведомления об ответах на комментарий совершенно неинформативны.

image
Упомянул в комментарии… В каком? Где?

В общем, пришлось покопаться еще…

IntenseDebate
  • Саппорт не отвечает на вопросы.
  • Сам виджет выглядит устаревшим.
  • Создается впечатление, что проект больше не поддерживается.

Cackle
  • Комментарии для каждого сайта стоят 500 рублей/год. Так как документации две, прийдется раскошелиться на два пакета. Если появится еще одна версия документации прийдется снова достать кошелек.
  • Название. Очень тяжело себя убедить пользоваться чем-то с названием “какл”.

SolidOpinion
  • Саппорт не отвечает на вопросы.
  • Проект выглядит заброшенным.

В итоге, самым адекватным (но не идальным) решением оказался неизвестный мне ранее Muut. У них есть бесплатная подписка, хороший саппорт, нет проблем с идентификаторами (опять я о них). Единственный минус — всё те же уведомления об ответах. Они приходят только тогда, когда у подписавшегося пользователя не открыто ни одной вкладки с Muut. Кроме этого, уведомления для админа отсутствуют (хотя заявлены, даже флажок есть в настройках). Приходится подписываться на все комментарии вручную и, на всякий случай, следить за новыми комментариям, заходя в админку. Посмотрим, что из этого выйдет. Пока что, пытаюсь бороться с саппортом, чтоб исправили некоторые неприятные баги.

Всё подряд или по одному? *


* Классу, методу, свойству

image

Новый сайт Матрешки был создан после взвешивания всех плюсов и минусов документаций к другим популярным и не очень популярным фреймворкам. Первым вопросом, который требовалось решить был выбор между двумя подходами в структурировании документации: размещение всех материалов на одной странице, либо же разбиение статей на отдельные страницы. Решением стал переключатель режимов (в верхней части меню есть кнопки “Все подряд” и “По одному”), оставляющий выбор на усмотрение посетителя.

«Материальный» дизайн


На сайте используется CSS фреймворк Materialize. К сожалению, все анимации в этом фреймворке основывались на манипуляциях с DOM, которые не благотворно влияют на производительность страницы (особенно на мобильных устройствах), поэтому, был использован только CSS и только необходимые части. Код, отвечающий за анимации (например, за выезжающее меню) пришлось переписать. В следующих версиях Materialize, надеюсь, исправят большинство подобных недоразумений.

Другие новости


  • Вся документация была вынесена из js файлов в отдельный репозиторий. Теперь .jsdoc файлы можно подключить в IDE отдельно.
  • Обновлен код TodoMVC и создана русскоязычная версия документации к коду, а исходник обрел собственный репозиторий.
  • Исходник к сайту и и генератору документации поменял своё местоположение.
  • Матрешка теперь размещена на двух CDN: cdnjs и jsdelivr (очень рекомендую этот CDN разработчикам библиотек и других скриптов, и спасибо jimaek, за помощь и быструю реакцию на вопросы).


Отдельное спасибо designiac который разработал новый логотип и принимал участие в создании сайта и Rendol, который помогает развивать проект, внося свои идеи и сообщая о неочевидных проблемах.

Следите за новостями в блоге. В среду на Хабре выйдет краткий туториал к Матрешке.

Спасибо за внимание. Всем добра.
Нужен ли проекту Твиттер для публикации кратких новостей?

Проголосовало 333 человека. Воздержалось 113 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Комментарии (18)


  1. getId
    06.04.2015 09:51
    +34

    Ура! Ещё один js framework.

    Та самая картинка
    image


  1. yurash
    06.04.2015 13:06
    +7

    На сайте используется CSS фреймворк Matreialize

    хоть и опечатка, но забавно получилось. Раз Matreshka, значит нужен не materialize а matreialize


  1. xxxTy3uKxxx
    06.04.2015 14:05

    Заголовки для TypeScript есть в планах?


    1. Finom Автор
      06.04.2015 14:25
      +1

      Пока нет, но Матрешка всегда открыта для пулл-риквестов.


      1. xxxTy3uKxxx
        06.04.2015 15:10
        +1

        Отлично, есть чем себя занять. Спасибо за хороший фреймворк


  1. lega
    06.04.2015 15:47

    В одном из предыдущих постов спрашивал и не получил ответа — есть ли возможность работать со вложенными объектами, например:

    var app = new Matreshka;
    app.bindNode( 'user.name', '.my-input' );
    app.user = {name: 'Matreshka'};
    app.user = {name: 'User'};
    

    Не могли бы вы добавить Matreshka.js в «местный» бенчмарк.


    1. Finom Автор
      06.04.2015 16:26

      Я бы лучше создал новый объект:

      var app = new Matreshka;
      app.user = new Matreshka;
      app.user.bindNode( 'name', '.my-input' );
      app.user.name = 'Matreshka';
      app.user.name = 'User';
      


      Вопрос по вашему бенчмарку добавил в список дел.

      У меня есть другой бенчмарк коллекций
      Вставка 10 элементов
      image
      Результат не самый лучший. Матрешка обгоняет Реакт только в ИЕ.

      Вставка 100 элементов

      Матрешка либо обгоняет либо стоит на ровне с другими фреймворками, кроме Ember. Странно, что самый быстрый (по слухам) React отстает местами от Angular.

      Вставка 1000 элементов
      image
      Все фреймворки позади (они даже не видны на графике из-за значений < 1 операций в секунду), кроме Ember. React еще больше удивил.

      Вывод можно сделать такой: при небольшом количестве элементов Матрешка немного отстает от других фреймворков но разница в скорости, как правило, не заметна при таком объеме данных. Начиная со ста элементов, Матрешка часто быстрее Реакта, «самого быстрого фрефмворка».

      Интересно, что, Матрешка выигрывает у всех в IE (извините, так получилось). Ember оказался самым быстрым из этого списка почти при любом раскладе.


      1. lega
        06.04.2015 18:23

        У меня есть другой бенчмарк коллекций
        Я уже комментировал этот тест на тостере, в нем много ошибок например для Ангуляра в такой ситуации нужно использовать track by иначе происходит построение нового DOM вместо его дополнения.
        Knockout.js нужно тестировать асинхронно, т.к. у него рендер идет через setTimeout.
        Ember не может быть самым быстрым т.к. он перерисовывает весь DOM, когда другие делают это «точечно».
        Все тесты влияют друг на друга, т.к. при запуске перед ними разное кол-во DOM. Тесты провоцируют разный рендеринг — где-то есть перенос, где-то нет, какие-то тесты «двигают» всю страницу — это бъет по производительности.
        самый быстрый (по слухам) React
        Он не самый быстрый, если перефразировать слова его разработчиков — React быстрее чем Ангуляр если тест для Ангуляра сделать не правильно.


    1. Finom Автор
      06.04.2015 16:40

      Если нужно использовать объект, то у Матрешки есть метод set (он может принимать объект).

      var app = new Matreshka;
      app.user = new Matreshka;
      app.user.bindNode( 'name', '.my-input' );
      app.user.set({name: 'Matreshka'});
      app.user.set({name: 'User'});
      



      1. lega
        06.04.2015 17:43

        Я просто хочу найти эквивалентный код, в ангуляре мне через ajax прилетают данные, я их просто помещаю в модель — все работает, например такой код:
        Полученный JSON:

        {
          "user": {
            "name": "User",
            "address": {
              "city": "Moscow"
            }
          }
        },
        

        Получаем данные и помещаем в scope.
        $.get('getUser', function(data) {
          scope.user = data.user;
          // call "digest" scope.$apply / scope.$scan
        })
        

        И на например «прибиндино» 2 поля:
        <div>{{user.name}} <br/> {{user.address.city}}</div>
        

        Я так понял что матрешка не рассчитана на работу с «иерархическими данными» (речь про данные, не про рендеринг), и в данном случае нужно за ранее создавать все возможные объекты, делать к ним биндинг, а для присвоения нужен врапер который будет копировать все значения.
        Хотя можно перенести все значения в один объект (в «корень»), но все равно это заставляет меня перемещать данные туда-обратно.


        1. Finom Автор
          06.04.2015 18:00
          +1

          Понял проблему. Как раз недавно возникла идея статичного метода toMatreshka (или просто to), которая конвертирует объект и его внутренности в экземпляры Матрешки. А для расширения такого объекта можно было бы воспользоваться аналогом merge из underscore.

          Установим дефольные значения объекту

          var app = MK.to({
          	user: {
          		name: '',
          		address: {
          			city: ''
          		}
          	}
          });
          
          
          app.user.address.bindNode( 'city', 'input' );
          
          ajax( function( data ) {
          	app.merge( data );
          });
          


          Как идея?

          Но подход вызывает вопросы:
          — Eсли внутри объекта содержится массив, заменять все элементы или добавлять?
          — Если в дереве объекта найден необъявленный ранее объект, конвертировать ли его в Матрешку или оставить обычным объектом?

          В общем, нужно подумать. Спасибо за то, что прояснили ситуацию. Такой возможности действительно сейчас нет.


  1. palkan
    06.04.2015 22:54

    В TodoMVC баг, однако: добавил два пункта, затем на одном кликнул на крестик – пишет, что «1 items left», а показывает два (Chrome, MacOS).


    1. Finom Автор
      07.04.2015 00:20

      Уже исправлено. Баг был связан с небольшим упущением при большом рефакторинге Matreshka.Array.


      1. palkan
        07.04.2015 13:25
        +2

        А может стоит покрыть код тестами, а уже потом выпускать версию 1.0?

        Вот все круто, но глупый баг в демо приложении и отсутстие тестов – это повод перестать рассматривать Матрешку для production целей.


        1. Finom Автор
          07.04.2015 13:56

          Автоматическое тестирование, конечно, будет. Но, в целом, Матрешка достаточно стабильна, а фичи обкатываются в продакшне по несколько месяцев перед выходом релиза. Спешка с рефакторингом — результат спешки со статьями, дабы удовлетворить условия оферты Хабра по блогу компании. Прошу прощения за неприятное недоразумение.


  1. StreetStrider
    10.04.2015 22:38
    +1

    Две вещи вызывают некоторое опасание:
    1. Использование собственных коллекций. Я понимаю, это нужно для более прямолинейной реализации биндинга через аксессоры, но множество других библиотек подобного типа умудряются делать такие вещи подкапотно (дельты / dirty-checking), так что модели пользователя (программиста) не трогаются. Я лично считаю, что очень важно уровень данных оставлять максимально независимым от библиотек. Это намного более лучшая интеграция и меньше отторжения у пользователя. Мне лично не нравится, что каждая либа считает вправе указывать мне какие модели использовать и как строить архитектуру.
    2. Вызывает опасение этот код: this.on( 'change:x', function() {, а именно строковой литерал. Каждый раз, как архитектура становится достаточно сложной, приходится конструировать эти строки, соединять все эти change/пространства имён/идентификаторы и тому подобное. Строковой литерал должен быть строковым литералом и лучше в строках важные детали архитектуры не хранить. То есть здесь явно две сущности: change — имя события, x — конкретное имя биндинга. Минимальное что можно сделать, это разбить их на два аргумента, тем более, я уверен, под капотом это всё равно делается.

    Да, не считайте это негативной критикой. Это просто мнение. Проект интересный, развивайте его дальше и желаю вам успехов.


    1. lega
      10.04.2015 23:58
      +1

      1. Использование собственных коллекций
      Не только коллекций, но и объектов, нельзя просто взять и «положить» «json объект», нужно все данные оборачивать в «обертки», хотя автор упомянул что подумает над решением.
      Я лично считаю, что очень важно уровень данных оставлять максимально независимым от библиотек.
      Поддерживаю, а то уже есть фреймворки у которых не только данные «в обертках», но «свой» DOM, свой ajax, свой сборщик и т.д.


    1. Finom Автор
      11.04.2015 16:15

      множество других библиотек подобного типа умудряются делать такие вещи подкапотно

      Тогда пропадет возможность использования всяких плюшек, типа делегированных событий, зависимостей свойств друг от друга и пр.