ВСЯ ВЛАСТЬ БЭКЕНДАМ!

Как вернуть власть на сервер или путь туда и обратно

speaker photo

Алексей Золотых (@zolotyh)

ВСЯ ВЛАСТЬ БЭКЕНДАМ!

Как вернуть власть на сервер или путь туда и обратно

Цель доклада

Опытные — улыбнитесь, начинающие — понимайте почему все так устроено

1991

HTML tags

The WWW system uses marked-up text to represent a hypertext document for transmision over the network. The hypertext mark-up language is an SGML format. WWW parsers should ignore tags which they do not understand, and ignore attributes which they do not understand of tags which they do understand.

Добавили form


<a href="http://www.example.com">link</a>
          

<form action="index_submit" method="post" accept-charset="utf-8">
  <label for="name">Name</label><input type="text" name="name" value="" >
  <button type="submit">Send</button>
</form>
          
django screen shot

От сайтов к приложениям

  • PHP 1995
  • Django 2003
  • Rails 2004
  • ZEND 2006
  • ASP.NET MVC 2007
  • Laravel 2011
Что было плохо?

XHR

XMLHttpRequest


let xhr = new XMLHttpRequest();

xhr.open('GET', '/article/xmlhttprequest/example/load');

xhr.send();

xhr.onload = function() {
  if (xhr.status != 200) {
    alert(`Error ${xhr.status}: ${xhr.statusText}`);
  } else {
    alert(`Success ${xhr.response.length} байт`);
  }
};
          

Эра jQuery


$("about").click(function(){
    $( "#result" ).load( "ajax/about.html";
});
          
проблемы jQuery

$("button").click(function(){
    $("#menu")
      .append('
  • New list item
  • '); }); $("li").click(function() { alert(1) })

    Был и такой костыль

    
    $("li").click(() => {...})
    
    $(document).on( "click", "li", () => {...});
              

    Backbone.js

    
    var Bookmark = Backbone.View.extend({
      template: _.template(...),
      render: function() {
        this.$el.html(this.template(this.model.attributes));
        return this;
      }
    });
              

    PubSub того времени

    
    book.on({
      "change:author": authorPane.update,
      "change:title change:subtitle": titleView.update,
      "destroy": bookView.remove
    });
              
    todo mvc screenshot

    AngularJS

    
    <input
      type="text"
      ng-model="yourName"
      placeholder="Enter a name here" />
    
    <h1>Hello {{yourName}}!</h1>
              

    Тот самый стейт

    todo mvc screenshot
    
    ...
    todoList.addTodo = function() {
      todoList.todos.push({
        text: todoList.todoText,
        done:false
      });
      todoList.todoText = '';
    };
    ...
              
    
    <li ng-repeat="todo in todoList.todos">
    ...
    </li>
              

    Gmail на React

    
    import { BrowserRouter as Router } from 'react-router-dom';
    
    ReactDOM.render(
      <StrictMode>
        <AuthProvider>
            <Router>
                  <App />
            </Router>
        </AuthProvider>
      </StrictMode>,
      document.getElementById('root'),
    );
              
    
    ...
    this.axiosInstance
      .interceptors
      .response.use(function (response) {
      return response;
    }, this.onReject);
    ...
    
    const onReject = (error: AxiosError): Promise<never> => {
      if (401 === error.response.status) {
        invalidateAuth();
        redirectToLogin();
        return Promise.reject(error);
      } else {
        return Promise.reject(error);
      }
    };
              

    Прежде чем увидеть письма придеться

    • Загрузить тонну JS
    • Проверить сессию
    • Выполнить роутинг
    • Загрузить необходимые данные
    • Отрисовать виджет

    — Как же SSR и NextJS?!

    — Все равно придется грузить тонну JS + гидрация

    А что если вернуть рендеринг на сервер?

    Нам потребуется

    • Ваш любимый фреймворк из прошлого
    • Stimulus
    • Turbo

    Stimulus

    stimulus.hotwire.dev

    
    
    
    // src/controllers/hello_controller.js
    import { Controller } from "stimulus"
    
    export default class extends Controller {
      static targets = [ "name" ]
    
      greet() {
        const element = this.nameTarget
        const name = element.value
        console.log(`Hello, ${name}!`)
      }
    }
              

    Как же все-таки решить проблему jQuery?

    
    $("button").click(function(){
        $("#menu")
          .append('
  • New list item
  • '); }); $("li").click(function() { alert(1) })

    Можно вешать события на Document

    
    $("li").click(() => {...})
    
    $(document).on( "click", "li", () => {...});
              

    MutationObserver

    
    <div contentEditable id="elem">Change <b>me</b></div>
    
    <script>
    let observer = new MutationObserver(mutationRecords => {
      console.log(mutationRecords); // console.log(изменения)
    });
    
    
    observer.observe(elem, {
      childList: true
      subtree: true,
      characterDataOldValue: true
    });
    </script>
              

    Turbo

    turbo.hotwire.dev

    iframe

    После отправки формы данный фрейм замениться на HTML, который пришлет сервер

    
    <turbo-frame id="new_message">
      <form action="/messages" method="post">
        ...
      </form>
    </turbo-frame>
              

    Ленивая загрузка

    
    
      

    This message will be replaced by the response from /messages.

    Обновления

    
    
      
    
    
    Демо...

    Недостатки

    • Маргинальная технология
    • Нестандартный подход
    • Вместо формата обмена данными HTML
    • Не подходит для особо динамичных интерфейсов

    Достоинства

    • Мало JS
    • Декларативная логика
    • Вся сложность собрана на беке, фронт простой
    • Очень быстрый рендеринг

    Спасибо!

    qr code
    twitter twitter.com/zolotyh
    telegram t.me/zolotyh