Evil Blocks — пишем лёгкий JS

Алексей Плуталов, Злые Марсиане

Evil Blocks
пишем лёгкий JS

Алексей Плуталов / фронтенд-инженер

+Alexey Plutalov / github/demiazz / @demiazz

Глава 1
Теория

Фреймворк — это идея

Книга «Getting Real» → Ruby on Rails

Популярные идеи JS

2-way data binding

      form ng-controller="Calculator"
        input type="number" ng-model="a"
        input type="number" ng-model="b"
        Sum: {{sum}}
    

Плата за связывание

Обзор data-binding в Angular и Ember.js: bit.ly/1bOeL2f

Рендеринг на клиенте и на сервере

Вида веба

Веб-сайты
  • Потребление информации;
  • Быстрый запуск;
  • Индексация;
  • Тонкий клиент.
Веб-приложения
  • Производство информации;
  • Работа офлайн;
  • Тонкий сервер.

Старайтесь не рендерить на клиенте и на сервере

Веб-сайты
  • Рендер на сервере;
  • На клиенте «оживляем» HTML;
  • JS простой и чистый.
Веб-приложения
  • Сервер — только JSON API;
  • Рендер на клиенте;
  • Сложная архитектура JS.

Идеи в Evil Blocks

Глава 2
Практика

Блоки

Атрибуты

      .m-popup.sk-sign-in@@popup
        .o-icon.sk-close@closeButton
        
        / эквивалентно
        
      .m-popup.sk-sign-in data-block="popup"
        .o-icon.sk-close data-role="closeButton"
    

Регистрация блока

      evil.block "@@popup",
        init: ->
          @adaptToScreen()
    

@@blockName транслируется в [data-block=blockName].

Методы

      evil.block "@@popup",
        open: ->
          @el.addClass("is-opened")
        close: ->
          @el.removeClass("is-opened")
    

@el ссылается на jQuery-объект текущей ноды.

События

Расширяем пример

      .m-popup.sk-sign-in-up@@popup
        .o-icon.sk-close@closeButton
        form.st-sign-in-form@form@signInForm
        form.st-sign-up-form@form@signUpForm
    

Для простоты, поля формы опущены, а сама форма упрощена.

События блока

      evil.block "@@popup",
        "on open": ->
          @open()
        "on close": ->
          @close()
    

События, как внешнее API, сильно снижают code coupling.

События элементов

      evil.block "@@popup",
        "ajax:beforeSend on @form": (e) ->
          e.el.find("::submit").addClass("is-inactive")
        "ajax:complete on @form": (e) ->
          e.el.find("::submit").removeClass("is-inactive")
    

Источник события привязывается к event.el.

Глобальные события

      evil.block "@@popup",
        "sign-in on body": ->
          @switchToSignInForm() and @open()
        "sign-up on body": ->
          @switchToSignUpForm() and @open()
    

$("body") выступает в роли event channel.

События окна

      evil.block "@@popup",
        "resize on window": ->
          @adaptToScreen()
        "message on window": (e, message) ->
          if @checkMessageDomain(message)
            @processMessage(message)
    

Взаимодействие

Множественные блоки

      .m-popup.sk-sign-in-up@@popup@@closable
        .o-icon.sk-close@closeButton
        form.st-sign-in-form@@remoteForm@signInForm
        form.st-sign-up-form@@remoteForm@signUpForm
    

Вертикальное взаимодействие

      evil.block "@@remoteForm",
        "ajax:success": ->
          @el.trigger("sent")
        
      evil.block "@@popup",
        "sent on @signInForm, @signUpForm": ->
          @close()
    

Горизонтальное взаимодействие

      evil.block "@@popup",
        open: ->
          $("body").trigger("close"); @show()
        
      evil.block "@@popup",
        open: ->
          $("@@popup").trigger("close"); @show()
    

Наследование

      evil.block "@@closeable",
        "click on @closeButton": ->
          @el.trigger("close")
        
      evil.block "@@popup",
        "on close": ->
          @close()
    

Рендеринг

Рендеринг на сервере

      evil.block "@@commentBox",
        "click on @commentButton": ->
          @commentForm.addClass("is-opened")
    

На сервере заранее формируется нужный HTML. На клиенте производится только переключение его видимости.

Рендеринг на сервере по запросу

      evil.block "@@commentBox",
        "ajax:success on @commentForm": (e, reply) ->
          replyEl = $(reply).appendTo(@replies)
          evil.block.vitalize(replyEl)
    

С сервера вместо JSON отправляется готовый HTML, с последующей инъекцией на страницу.

Рендеринг на клиенте

      evil.block "@@commentBox",
        "ajax:success on @commentForm": (e, reply) ->
          replyHtml = JST["comment"](reply)
          replyEl = $(replyHtml).appendTo(@replies)
          evil.block.vitalize(replyEl)
    

Классический пример рендеринга на клиенте.

Интеграция

      evil.block "@@react",
        init: ->
          cmpName = @el.data("react-component")
          cmpData = @el.data("react-data") || { }
          React.renderComponent(
            evil.components[cmpName](cmpData), @el.get(0)
          )
    

Twitter Flight

Twitter Flight

Вопросы?