Slide 1

Slide 1 text

Фронтенд без фронтенда Владимир Дементьев

Slide 2

Slide 2 text

palkan_tula palkan github.com/palkan 2

Slide 3

Slide 3 text

palkan_tula palkan Разработка сегодня 3 Это я

Slide 4

Slide 4 text

palkan_tula palkan Ты помнишь, как всё было десять лет назад 4 Full-stack developer Это тоже я

Slide 5

Slide 5 text

palkan_tula palkan 5 Full-stack разработка на Ruby (on Rails) в 202

Slide 6

Slide 6 text

palkan_tula palkan Full-stack ruby? Saint P Ruby Community @ Telegram 6

Slide 7

Slide 7 text

palkan_tula palkan 7 Одного программиста достаточно, чтобы вкрутить лампочку Full-stack — это ...

Slide 8

Slide 8 text

palkan_tula palkan 8 Разработка в рамках единой экосистемы Full-stack — это ...

Slide 9

Slide 9 text

Немного истории А вот в наше время...

Slide 10

Slide 10 text

palkan_tula palkan Full-stack Rails 10 HTML-over-the-Wire

Slide 11

Slide 11 text

HTML (Haml/Slim) Helpers

Slide 12

Slide 12 text

palkan_tula palkan Helpers 12 https://github.com/redmine/redmine

Slide 13

Slide 13 text

HTML (Haml/Slim) CoffeeScript jquery Helpers jquery-ujs

Slide 14

Slide 14 text

palkan_tula palkan jquery-ujs 14 # show.html.slim = link_to "Delete", post_path(post), remote: true # destroy.js.erb $("#<%= dom_id(post) %>").remove();

Slide 15

Slide 15 text

HTML (Haml/Slim) Asset Pipeline CoffeeScript jquery Helpers jquery-ujs Turbolinks Sass Bootstrap Bundler (asset gems) vendor/assets

Slide 16

Slide 16 text

palkan_tula palkan Перемены на фронтé 16

Slide 17

Slide 17 text

HTML (Haml/Slim) Asset Pipeline CoffeeScript jquery Helpers jquery-ujs Turbolinks Sass Bootstrap Bundler (asset gems) vendor/assets npm / yarn ES6 Webpack PostCSS React SPA API

Slide 18

Slide 18 text

palkan_tula palkan Эволюция фронтенда Приводит к разделению разработчиков на бекенд и фронтенд «группировки» Вытесняет Ruby/Rails на роль поставщика API Увеличивает стоимость разработки и поддержки* 18 * Примечание: мнение автора может не совпадать, а может и совпадать

Slide 19

Slide 19 text

palkan_tula palkan 19 Возможна ли разработка современных веб-приложений, не выходя из зоны комфорта Ruby и Rails

Slide 20

Slide 20 text

palkan_tula palkan 20 Живая «классика»

Slide 21

Slide 21 text

palkan_tula palkan 21 Неоклассицизм

Slide 22

Slide 22 text

palkan_tula palkan hey.com 22

Slide 23

Slide 23 text

palkan_tula palkan 23 Client-side rendering Server-side rendering

Slide 24

Slide 24 text

palkan_tula palkan 24 Client-side rendering Server-side rendering SPA Turbolinks

Slide 25

Slide 25 text

palkan_tula palkan Turbolinks SPA для бедных HTML приложений Перехватывает переходы по ссылкам, запрашивает страницы асинхронно (AJAX), заменяет HTML (только body) Использует собственный кэш для создания ощущения мгновенных переходов 25

Slide 26

Slide 26 text

palkan_tula palkan Architecture Reactivity rails-ujs 26 Client-side rendering Server-side rendering Interactivity JS framework SPA Turbolinks JS sprinkles

Slide 27

Slide 27 text

palkan_tula palkan Architecture Reactivity rails-ujs Stimulus 27 Client-side rendering Server-side rendering Interactivity JS framework JS sprinkles SPA Turbolinks

Slide 28

Slide 28 text

palkan_tula palkan Stimulus 28 stimulusjs.org

Slide 29

Slide 29 text

palkan_tula palkan Пример 29 Скрываемые нотификации

Slide 30

Slide 30 text

palkan_tula palkan Пример: jQuery 30 function initBannerClose(){
 // Кажется, у нас CSS «утёк» $('.banner --close').click(function(e){ e.preventDefault(); const banner = $(this).parent(); banner.remove(); }); }); $(document).on('load', initBannerClose); // Не забываем про Turbolinks $(document).on('turbolinks:load', initBannerClose); // ...и jquery-ujs $(document).on('ajax:success', initBannerClose);

Slide 31

Slide 31 text

palkan_tula palkan Пример: Stimulus 31

AnyWork ...

import { Controller } from "stimulus"; export class BannerController extends Controller { hide() { this.element.remove(); } }

Slide 32

Slide 32 text

palkan_tula palkan Stimulus: сильные стороны Автоматическая активация стимулов (MutationObserver API) Turbolinks работает без лишних усилий 32

Slide 33

Slide 33 text

palkan_tula palkan Stimulus Позволяет делать из статичного HTML элемента компонент ...но писать его придётся ручками ...или нет 33

Slide 34

Slide 34 text

palkan_tula palkan 34 Пример Интерактивная форма (Stimulus + Vue)

Slide 35

Slide 35 text

palkan_tula palkan 35 = form_for @form do |f| // ... .field label.label Started at .control data-controller="datetimepicker" data-tz="-04:00" = f.text_field :started_at, required: true .field label.label Location .control data-controller="location-input" = f.text_field :location, placeholder: "Enter event location ..." .field label.switch = f.check_box :waitlist_enabled span.check span.control-label.label Waitlist enabled .field label.label Cover Image .control data-controller="upload" data-url=blob_path(f.model.cover) = f.file_field :cover Пример

Slide 36

Slide 36 text

palkan_tula palkan 36 export class VueInputController extends Controller { connect() { var el = this.element; this.vue = new Vue( { el, data() { // Все data-атрибуты станут data.props Vue-компонента return { props: el.dataset }; }, // Вместо самого элемента будет контент компонента template: '' } ); } disconnect() { if (this.vue) { this.vue.$destroy(); } } } Пример

Slide 37

Slide 37 text

palkan_tula palkan Stimulus + Vue 37 github.com/gretchenfitze/stimulus-turbolinks

Slide 38

Slide 38 text

palkan_tula palkan Больше примеров stimulusconnect.com betterstimulus.com github.com/stimulus-use/stimulus-use 38

Slide 39

Slide 39 text

Ты же говорил, что мы не будем писать на JavaScript!

Slide 40

Slide 40 text

palkan_tula palkan Architecture Reactivity rails-ujs Stimulus 40 Client-side rendering Server-side rendering Interactivity JS framework SPA Turbolinks JS sprinkles HTML-over-WebSocket

Slide 41

Slide 41 text

palkan_tula palkan Phoenix LiveView 41

Slide 42

Slide 42 text

palkan_tula palkan Phoenix LiveView HTML-компонент «присоединяется» к процессу на сервере (через сокет) Процесс реагирует на сообщения из браузера или других процессов, вычисляет изменившиеся части шаблона и отправляет их клиенту Клиент использует morphdom для изменения DOM 42

Slide 43

Slide 43 text

palkan_tula palkan “A new way to craft modern, reactive web interfaces with Ruby on Rails.” 43

Slide 44

Slide 44 text

palkan_tula palkan 44 автор Stimulus Reflex CableReady

Slide 45

Slide 45 text

palkan_tula palkan CableReady Библиотека для изменения DOM с сервера Использует для транспорта Action Cable broadcast Использует morphdom на клиенте 45

Slide 46

Slide 46 text

palkan_tula palkan Пример 46
... <%= button_to item_path(item), method: :delete, remote: true do %> ... <% end %>

Slide 47

Slide 47 text

palkan_tula palkan Пример 47 # items_controller.rb def destroy item.destroy! stream = ListChannel.broadcasting_for(item.list) cable_ready[stream].remove(selector: dom_id(item)) head :no_content end

Slide 48

Slide 48 text

palkan_tula palkan CableReady 48

Slide 49

Slide 49 text

palkan_tula palkan StimulusReflex Рефлексы (объекты) реагируют на действия пользователя и рендерят HTML CableReady отправляет HTML клиенту и обновляет DOM 49

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

palkan_tula palkan Пример 51

Slide 52

Slide 52 text

palkan_tula palkan Пример 52
data-reflex="change ->List#toggle_item_completion" data-item-id="<%= item.id %> > ...

<%= item.desc %>

...

Slide 53

Slide 53 text

palkan_tula palkan Пример 53 class ListReflex < ApplicationReflex def toggle_item_completion item = find_item item.toggle!(:completed) html = render_partial("items/item", {item}) selector = dom_id(item) cable_ready[ ListChannel.broadcasting_for(item.list) ].outer_html( **{selector, html}) cable_ready.broadcast morph_flash :notice, "Item has been updated" end private def find_item Item.find element.dataset["item-id"] end end

Slide 54

Slide 54 text

palkan_tula palkan Пример 54 class ListReflex < ApplicationReflex def toggle_item_completion item = find_item item.toggle!(:completed) html = render_partial("items/item", {item}) selector = dom_id(item) cable_ready[ ListChannel.broadcasting_for(item.list) ].outer_html( **{selector, html}) cable_ready.broadcast morph_flash :notice, "Item has been updated" end private def find_item Item.find element.dataset["item-id"] end end Изменяем DOM элемента всем подключенным пользователям (и себе) Показываем flash-сообщение текущему пользователю Объект-представление текущего элемента

Slide 55

Slide 55 text

palkan_tula palkan Пример 55 class ApplicationReflex < StimulusReflex ::Reflex private def morph_flash(type, message) morph "#flash", render_partial( "shared/alerts", {flash: {type => message}} ) end end

Slide 56

Slide 56 text

palkan_tula palkan Больше рефлексии futurism — асинхронная загрузка элементов страницы optimism — серверная валидация форм в реальном времени 56

Slide 57

Slide 57 text

palkan_tula palkan И немного альтернативы 57 github.com/unabridged/motion

Slide 58

Slide 58 text

palkan_tula palkan SR: сильные стороны Стабильный проект (v3.4) Одна из лучших документаций в мире OSS Активное сообщество в сети (Discord >700 участников, многочисленные посты и видео) 58

Slide 59

Slide 59 text

palkan_tula palkan Reactive Rails! medium.com/@obie/react-is-dead-long-live-reactive-rails-long-live-stimulusreflex-and-viewcomponent-cd061e2b0fe2 59

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

palkan_tula palkan SR: слабые стороны Action Cable Большой объём данных по сети (нет механизма диффов как в LiveView) Данные всегда идут через pub/sub (даже если нужно отправить клиенту- инициатору) 61 AnyCable

Slide 62

Slide 62 text

palkan_tula palkan 62 Весь наш UI — это просто HTML. Как нам его организовать?

Slide 63

Slide 63 text

palkan_tula palkan Architecture Reactivity rails-ujs Stimulus 63 Client-side rendering Server-side rendering Interactivity JS framework SPA Turbolinks JS sprinkles HTML-over-WebSocket

Slide 64

Slide 64 text

palkan_tula palkan evilmartians.com/blog evilmartians.com/chronicles/evil-front-part-1 64

Slide 65

Slide 65 text

palkan_tula palkan 65 Компонентный подход

Slide 66

Slide 66 text

palkan_tula palkan Architecture Reactivity rails-ujs Stimulus 66 Client-side rendering Server-side rendering Interactivity JS framework SPA Turbolinks JS sprinkles HTML-over-WebSocket View Components

Slide 67

Slide 67 text

palkan_tula palkan View Components 67 partials decorators helpers facades presenters builders view components

Slide 68

Slide 68 text

palkan_tula palkan View Component 68 "Ruby objects that output HTML" View Model (класс) + шаблон Изолированность, тестируемость, переиспользование Made by GitHub github.com/github/view_component

Slide 69

Slide 69 text

palkan_tula palkan Пример 69 # app/components/button/component.rb class Button ::Component < ViewComponent ::Base attr_reader :label, :icon def initialize(label:, icon: nil) @label = label @icon = icon end alias icon? icon end

Slide 70

Slide 70 text

palkan_tula palkan Пример 70 # app/components/button/component.html.erb <% if icon? %> <%= icon %> <% end %> <% == label %>

Slide 71

Slide 71 text

palkan_tula palkan Пример 71 # some.html.erb
<%= render Button ::Component.new( label: "Like", icon: "❤") %>

Slide 72

Slide 72 text

palkan_tula palkan Пример 72 # app/components/like_button.rb class LikeButton < Button ::Component def initialize super(label: I18n.t("like"), icon: "❤") end end # some.html.erb
<%= render LikeButton.new %>

Slide 73

Slide 73 text

palkan_tula palkan Пример 73 # test/components/button_test.rb class Button ::ComponentTest < ActiveSupport ::TestCase include ViewComponent ::TestHelpers def test_render render_inline Button ::Component.new(label: "Test") assert_selector "button.btn", text: "Test" assert_no_selector "button.btn i" end def test_render_with_icon render_inline Button ::Component.new( label: "Test", icon: "✔") assert_selector "button.btn", text: "Test" assert_selector "button.btn i", text: "✔" end end

Slide 74

Slide 74 text

Слыхал, Ник, GitHub переизобрели Cells Долго они

Slide 75

Slide 75 text

palkan_tula palkan View Component Тесная интеграция с Rails (Rails way) Скорость рендеринга (до 10x быстрее partials) Функционал превью 75

Slide 76

Slide 76 text

palkan_tula palkan View Component 76 app/ frontend/ components/ banner/ component.rb component.html.slim component.scss component.js Весь компонент в одной папке

Slide 77

Slide 77 text

palkan_tula palkan Другие компоненты Cells hanami-view dry-view komponent elemental_components 77

Slide 78

Slide 78 text

palkan_tula palkan 78 Client-side rendering Server-side rendering JS framework SPA Turbolinks JS sprinkles HTML-over-WebSocket View Components CSS-in-JS

Slide 79

Slide 79 text

palkan_tula palkan CSS 2020 Bulma (+Buefy) Tailwind Shoelace 79

Slide 80

Slide 80 text

palkan_tula palkan Mobile-first CSS-only Модульность и кастомизация (Sass- переменные) Часто используется в связке с Vue компонентами (Buefy) 80

Slide 81

Slide 81 text

palkan_tula palkan Utility-first (никаких компонентов, только набор классов) Удобный механизм выделения компонентов Быстрое прототипирование (+playground) Встроенные оптимизации сборки 81

Slide 82

Slide 82 text

palkan_tula palkan 82

Slide 83

Slide 83 text

palkan_tula palkan Web Components Продуманная кастомизация Accessibility 83

Slide 84

Slide 84 text

palkan_tula palkan 84

Slide 85

Slide 85 text

palkan_tula palkan Что выбрать? Bulma — для админок и особенно, если есть Vue Shoelace — CRUD, админки Tailwind — для пользовательских интерфейсов 85

Slide 86

Slide 86 text

palkan_tula palkan HTML-over-WebSocket (StimulusReflex, etc.) JS sprinkles (Stimulus) Fake SPA (Turbolinks) Компонентный подход (ViewComponent, komponent, etc.) Гибкий CSS-фреймворк (Tailwind, Shoelace, Bulma) 86 Full stack

Slide 87

Slide 87 text

Итоги Есть ли фронтенд без фронтенда?

Slide 88

Slide 88 text

palkan_tula palkan Фронтенд без фронтенда Способен заменить JS фреймворки/ SPA для приложений с нехитровыдуманным UI Повышает продуктивность (но всё же имеет порог вхождения) Новая эра только начинается, всё лучшее — впереди! 88

Slide 89

Slide 89 text

Cпасибо! @palkan @palkan_tula evilmartians.com @evilmartians