Привет, Хабр.

Некоторое время назад наткнулся на интересную статью в блоге Кристофера Артмана, которой он сравнивает до чего эволюционировал Реакт в наше время и задается вопросом, о том, не вернулись ли мы в отправную точку, и Реакт на сервере - это старый добрый PHP.

Кроме самой статьи мне интересно было почитать комментарии к ней. И там довольно большое количество людей не согласилось с автором.

Под катом Вы найдете вольный перевод (или даже рерайт) этой статьи, а с оригиналом можно ознакомится по ссылке.

Серверный JavaScript — это просто PHP с новыми обертками?

Возвращение серверного рендеринга (SSR) в мире JavaScript породило бурные обсуждения среди разработчиков. "Разве это не просто PHP на новый лад?" — задаются вопросом скептики. На первый взгляд сравнение может показаться уместным, но в действительности все гораздо сложнее и интереснее.

Давайте разберемся, почему современный серверный JavaScript — это не шаг назад, а огромный скачок вперед в разработке мощных и эффективных веб-приложений.

Вспоминая PHP: простота былых времен

Когда-то, в эпоху Internet Explorer, PHP был основным инструментом для создания серверной логики. Мы писали серверный код, генерировали HTML на лету и отправляли его пользователям. Всё казалось таким простым. Но тогда и задачи были куда менее амбициозными. Мы решали небольшие проблемы, создавали статичные страницы или примитивные блоги.

Типичный стек включал серверный язык (PHP, Java или даже Perl), рендеринг HTML на сервере и минимум JavaScript на клиенте. Интерфейс был довольно простым, а интерактивность добавлялась точечно, с помощью "ненавязчивого JavaScript" — помните такое? ? Тогда мы избегали писать слишком много JS, и это было скорее облегчением, чем ограничением.

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

Почему мы ушли от серверного рендеринга

Когда приложения становились сложнее, старые подходы начали трещать по швам. Представьте себе блог с системой комментариев. На сервере вы рендерите все комментарии в HTML. А теперь добавьте нового комментария с клиента: нужно либо заново прописывать HTML-структуру в JavaScript, либо использовать хитрые трюки вроде скрытых шаблонов. Это был настоящий головняк, особенно если HTML-классы менялись.

Более того, императивный подход к созданию интерфейсов (когда вы напрямую управляете DOM) оказался крайне неудобным. Нам нужен был декларативный подход и компоненты, чтобы состояние и UI всегда оставались синхронизированными.

Вот так мы пришли к клиентским приложениям (SPA). Они дали нам богатую интерактивность и гибкость... но вместе с ними и новый набор проблем.

Проблемы SPA: назад к серверу?

Полный перенос логики на клиент привел к множеству трудностей:

  1. Навигация. Простые URL с серверным рендерингом сменились сложными роутинг-фреймворками. Да, мы получили такие фишки, как сохранение состояния между страницами, но какой ценой? Сколько "ссылок" вы видели, которые по сути были кнопками с onClick? ?‍♂️

  2. Производительность. Устройства пользователей теперь обрабатывали весь интерфейс. Рендеринг пустого div с подключением скрипта для каждой страницы плохо сказывался на SEO и скорости загрузки.

  3. Разделение команд. SPA усилили разрыв между фронтенд- и бэкенд-разработчиками. Раньше мы все были "веб-разработчиками" и работали на всех уровнях стека. Теперь же это разделение ухудшило коммуникацию и, как следствие, качество конечного продукта.

Современные фреймворки: не шаг назад, а движение вперед

Но вот хорошие новости: мы возвращаемся к серверу, но не к PHP. Современные фреймворки вроде Next.js, Remix и SvelteKit позволяют использовать единую экосистему JavaScript для разработки. Мы рендерим UI на сервере, но делаем это декларативно, с помощью компонентов и состояний.

Может показаться, что React Server Components с SQL-запросами похожи на старый добрый PHP с его смешением HTML, CSS и SQL. Но главное отличие в том, что мы больше не создаем "спагетти-код". Мы используем проверенные временем принципы разработки UI и современные инструменты.

Фреймворки также стирают границу между фронтендом и бэкендом. Теперь один разработчик может реализовать функциональность "от и до": от запросов к базе данных до UI-интеракций. Это не только упрощает процесс, но и делает продукты более целостными и оптимизированными.

Новый рассвет веб-разработки

Сегодня мы берем лучшее из мира PHP и усиливаем это мощью современного JavaScript. Мы можем строить амбициозные приложения, используя сервер не только для сериализации JSON, но и для оптимального рендеринга.

Это также возвращает нас к идеалу "фулстек-разработчика", только теперь у нас есть инструменты, чтобы делать это красиво и эффективно. Если вы чувствуете, что застряли в рамках "фронтенда" или "бэкенда", самое время расширить горизонты и стать мастером на обеих сторонах стека.

Мы прошли долгий путь от вставок PHP в HTML. Да, это может выглядеть похоже на поверхности, но теперь мы строим на основе многолетнего опыта и лучших практик.

Заключение

А что вы думаете об этой тенденции? Уже используете фуллстек JavaScript-фреймворки? Или все еще держитесь за PHP? Пишите в комментариях, обсудим! ?

А если вам интересны современные подходы в разработке, вы хотите больше узнать о JavaScript, трендах и интересных проектах, приглашаю в мой Telegram-канал! Там я делюсь опытом, новостями и полезными материалами, а еще стараюсь сделать контент не только информативным и интересным.

Присоединяйтесь: https://t.me/+qbK9ZPuAocI2MWUy ?

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


  1. karrakoliko
    11.01.2025 17:21

    главное отличие в том, что мы больше не создаем "спагетти-код".

    поперхнулся смузи

    Мы используем проверенные временем принципы разработки UI и современные инструменты.

    php'шному symfony 20 лет, реакту около 10, а серверному (ну так, чтобы в проде, а не в бложиках) и того меньше. ни современности, ни старых надежных принципов

    про js и "проверенные временем принципы разработки" особенно смешно


  1. JBFW
    11.01.2025 17:21

    А теперь добавьте нового комментария с клиента: нужно либо заново прописывать HTML-структуру в JavaScript, либо использовать хитрые трюки вроде скрытых шаблонов. Это был настоящий головняк, особенно если HTML-классы менялись.

    /get_new_comment -> (HTML-block) -> element.innerHTML = result

    Головняк - обалдеть. Не, ну можно конечно на каждый пук создавать DOM в браузере заново (и многие так и делали)

    Результат мы все видели (жаль, что не все уже помнят, что этого можно было избежать):


  1. tuxi
    11.01.2025 17:21

    SSR должен был вернуться и он вернулся. Жаль только, что в статье js рассматривается как единственный теперь язык (экосистема и тп).


  1. CaptGg
    11.01.2025 17:21

    В статье почему-то не написано самое существенное чем Next.js отличается от PHP. Каким образом стираются границы. Про единый код рендеринга как на сервере, так и клиенте. Про прозрачные для разработчика обновления данных на клиенте.

    Писать "спагетти-код" на React тоже можно, ровно как и хорошо организовать код на PHP. А вот преимущества работы Next.js проекта как единого приложения с общим кодом одновременно на сервере и клиенте на PHP не получить, даже с htmlx.


  1. tolyanski
    11.01.2025 17:21

    Или все еще держитесь за PHP? 

    В самом PHP уже 100500 лет как не принято делать серверный рендеринг страниц. Более того, деление на фронт и бек добавляет гибкости, когда появляется возможность в как можно меньшем числе частей системы использовать такое г.. как JavaScript, и на бэкенде появляется большая свобода выбора между PHP, Go, .NET, Python, да хоть C++.
    Кроме того, в проектах чуть сложнее магазинчика "У Васи", имеется тенденция делать несколько разных фронтендов, не только веб, но и нативные мобильные клиенты например. И вот нативным мобильным приложениям этот ваш серверный рендеринг не уперся от слова совсем.


  1. bolk
    11.01.2025 17:21

    Типичный стек включал серверный язык (PHP, Java или даже Perl)…

    У людей как будто память стёрли. Никто не помнит ASP и что сайты писали на том же JavaScript (ну и VBScript ещё) что ли? Ещё Netscape выпускал сервер, где использовался JavaScript, там ещё тег был <SCRIPT RUNAT=SERVER>.


  1. kAIST
    11.01.2025 17:21

    Скоро интересно появятся статьи: Это революционно! Можно писать приложения, которые работают без интернета и не в браузере!


    1. Atorian
      11.01.2025 17:21

      Это вы про те, которые начали писать когда node.js появился?)


      1. Mr_Cheater
        11.01.2025 17:21

        Нет:) ИМХО. Это про те, которые компилируются и запускаются с хард драйва/ссд.


    1. senchik
      11.01.2025 17:21

      жду не дождусь, и так же "инновация": смотрите можно писать не веб приложения на электроне...


  1. AlexLeonov
    11.01.2025 17:21

    >> А что вы думаете об этой тенденции? 

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

    Так уж повелось, что код на JS исполняется браузерами. Быстро и хорошо исполняется. Отлично. Эта ситуация останется еще на десятки лет доминирующей.

    Но заставлять живых людей писать на JS и даже придумывать целые "фреймворки" (не являющиеся таковыми) для этого?

    Нет, это не магистраль. Магистраль - это судьба JS, как языка промежуточного представления кода. Как нового ассемблера.

    Посмотрите, к примеру, на Symfony Live Components, библиотеку, позволяющую бэкенд-разработчику создавать сложные и "живые" интерфейсы, не написав ни строчки кода на JS вручную.

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

    Между прочим, так примерно и развивается IT последние лет 50. Ничего нового.


    1. Alexandroppolus
      11.01.2025 17:21

      В современном "большом" фронте уже несколько лет как отказались от js в пользу ts. Вполне себе настоящий язык, с довольно продвинутой системой типов


  1. ALapinskas
    11.01.2025 17:21

    Реакт на сервере - это старый добрый PHP.

    По моему, вставки html в код - это не react, а jsx.

    Чтобы его использовать, как любые другие библиотеки нужно сначала подключить, настроить сборщик. Для использования php подключать/собирать ничего не нужно, работать с php гораздо проще.


  1. ednersky
    11.01.2025 17:21

    А теперь добавьте нового комментария с клиента: нужно либо заново прописывать HTML-структуру в JavaScript, либо использовать хитрые трюки вроде скрытых шаблонов. Это был настоящий головняк, особенно если HTML-классы менялись.

    не помню, чтобы в этом месте был "настоящий головняк".

    Просто придерживались какой-то единой схемы назначения имён классам, выдачи айдишников сущностям и, собственно всё.

    а головняк начался, когда перешли к всем этим реактивностям:

    • среднестатистический сайт не выдерживает Refresh

    • среднестатистический сайт о 200кБ размере страницы требует 16 ядерного 10ГГц процессора

    • среднестатистический сайт ломает кнопку браузера "назад"

    • на компонентах зачем-то попереписывали многие системные понятия, например "текстовый редактор" и в итоге среднестатистический редактор имеет массу багов. Вот, например прямо тот, в котором я пишу этот текст, теряет фокус, но не понимает этого, поэтому курсор в нём иногда есть, иногда нет. Далее, он криво синтегрирован с буфером обмена: никогда не угадаешь сработает ли Ctrl-C или Ctrl-Insert или нет

    • и так далее

    Более того, императивный подход к созданию интерфейсов (когда вы напрямую управляете DOM) оказался крайне неудобным.

    и поэтому написали параллельное DOM дерево. ага-ага. да-да-да

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

    • кому нужен?

    • зачем нужен?

    • и где вы увидели декларативный подход, например в React?


  1. ednersky
    11.01.2025 17:21

    Но главное отличие в том, что мы больше не создаем "спагетти-код".

    что за "спагетти-код"? не было такого в PHP, а вот вся эта возня с промисами была в JS. сейчас она выродилась в появление оператора await, и вот эту проблему:


    1. peterpro
      11.01.2025 17:21

      Не очень понял ни комикс, ни проблему.


      1. ednersky
        11.01.2025 17:21

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

        эта штука зовётся асинхронным программированием, ну и никак с реактом не связана - реакт не улучшил, не ухудшил и не использовал асинхронный програминг.

        ну и в асинхронном программировании есть два путя как вылечить спагетти-код:

        • промисы/монады (сохраняем колбеки, но извращаемся)

        • выпрямляем на уровне языка (здесь снова два путя: аsync и fullstack)

        ну и вот JS как-то (пусть и плохо) да выпрямил спагетти. но с React это никак не связано. Ну а почему плохо - вот коммикс.

        если под проблемой спагетти имелось в виду чтойта другое, то у меня, увы, даже нет предположений что.

        react решал ровно одну проблему:

        разработчков претило писать update_foo(bar) они прямо хотели-хотели вместо этого писать foo = bar. ХЗ почему, но именно ради этой семантики реакт и задуман.


        1. peterpro
          11.01.2025 17:21

          Простите, не очень точно наверное выразился - я всё ещё не понимаю суть комикса (почему все остальные становятся сиреневыми?) и проблемы await.


          1. ednersky
            11.01.2025 17:21

            ах это, я всё ещё полагал, что мы о реакте пытаемся поговорить

            ну а у асинхронности, как я сказал выше есть пара-тройка вариантов её реализации

            • колбеки: промисы/монадки.

            • async/await - это по сути надстройка над предыдущим вариантом. по факту функция как возвращала промис так и возвращает, а оператор await его ждёт

            • ну и fullstack

            так вот, если говорить об await, то (это необязательное требование, но текущие реализации (именно реализации) делают его обязательным) чтобы функция могла позвать await на другую функцию, она сама должна быть помечена как async.

            соответственно невозможно смешивать async и не async функции и как только появляется хоть один await (async) приходится обмазывать этими операторами всё подряд (отсюда комикс).

            именно в этом месте внедрение async/await становится несовместимо с легаси, рассчитанным на однопоточный код. Не было бы этих операторов (был бы, например, fullstack), то можно было бы на асинхронность переписать драйвера (скажем http или даже socket уровня) и использовать старый код. Но увы


            1. peterpro
              11.01.2025 17:21

              приходится обмазывать этими операторами всё подряд (отсюда комикс).

              Не все подряд, а только вышестоящие функции / методы. И все еще не понимаю проблемы.

              однопоточный код

              Он и остается однопоточным, event loop != многопоточность.

              fullstack

              Что вы имеете в виду под этим словом? Backend?


              1. ednersky
                11.01.2025 17:21

                Не все подряд, а только вышестоящие функции / методы. И все еще не понимаю проблемы.

                ну да, а этого мало?

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

                https://endpoint/foo

                Далее у вас есть над этим клиент

                response = client.get(url)

                Далее над этим есть свой клиент, например s3

                object = s3.get(bucket, key, version)

                над этим есть ещё один клиент, например хранит профайлы юзеров

                user = profiles.get(user_id)

                и вся эта возня находится в каком-нибудь, ну, например, чат-боте

                ии вот у вас выбор: если Вы client.get делаете async, то погнали дальше переписывать и s3.get и profile.get и mainloop чат-бота тоже.

                то бишь практически всё что у вас есть вы переписали.

                а был бы вместо async/await обычная корутина, работающая без дополнительных ключевых слов, то всё это можно было бы просто переиспользовать as is переписав только одно место - хождение в сеть


                1. peterpro
                  11.01.2025 17:21

                  обычная корутина

                  Вот я этого не понимаю - что вы имеете в виду под "корутиной" в контексте JS?

                  Мы вообще сейчас про серверную часть говорим?


                  1. ednersky
                    11.01.2025 17:21

                    Мы вообще сейчас про серверную часть говорим?

                    а есть ли разница? нет, разницы нет. технология одна и та же, а применяется хоть на сервере, хоть на клиенте. клиент ведь тоже делает http и иные запросы.

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

                    Вот я этого не понимаю - что вы имеете в виду под "корутиной" в контексте JS?

                    ну я имел в виду корутину переключающую управление ключевыми словами вроде yield или cede (в зависимости от обстоятельств делают так или этак)

                    В общем случае код внутри корутины может (и должен) не знать о других корутинах. Это становится интересно ну разве что около сети или около долговыполняющихся запросов. А потому обмазывание всего подряд async/await'ами как бы не вдохновляет.


  1. ednersky
    11.01.2025 17:21

    Мы рендерим UI на сервере, но делаем это декларативно, с помощью компонентов и состояний.

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

    самый яркий пример - операционная система среднего ноутбука.

    вот был простой и понятный как топор SysV init. В нём был императивный bash.

    чтобы выучить bash пользователь должен был разобраться с десятью (прописью: десятью) операторами и нюансами.

    но пришли декларативщики: "это сложно!", "это непонятно!", "мы будем писать юниты декларативно!"

    и что же они сделали? Монстра!

    Одно только перечисление списка возможных деклараций в юните systemd занимает список о ~6500 (прописью: более шести тысяч!) деклараций:

    https://www.freedesktop.org/software/systemd/man/latest/systemd.directives.html

    This index contains 6467 entries in 24 sections, referring to 413 individual manual pages.

    и так В КАЖДОМ случае внедрения деклараций.

    затем приходит "папочка с топориком и равняет то, что сделал сынка с рубанком".

    в SQL так появился PL/SQL. в реактивностях появляется, наконец, серверный рендеринг и в будущем выкинут на помойку сами реактивности, вернувшись к нормальному DOM,

    а вот что нас ждёт с systemd не знаю. думаю, не доживу уже до развязки.


    1. Dadadam999
      11.01.2025 17:21

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

      Systemd правда хороший пример монстра.


      1. ednersky
        11.01.2025 17:21

        Systemd правда хороший пример монстра.

        современный реактивный фронтенд — другой пример такого же монстра


    1. alex8079
      11.01.2025 17:21

      Нуу... Реактивность можно и на реальном DOM делать. От этого она не престанет порождать кучу глюков (с точки зрения конечного пользователя), не перестанет требовать в разы бОльшего времени на ручное тестирование (любое другое всегда было пустой тратой времени) для того, чтобы выявить хотя бы десятую часть тех багов с которыми столкнется end user (нет, это не корнер кейсы... ВНЕЗАПНО!).

      Ответственные за сайт Озона годами не могут решить проблему рандомного отображения первой страницы (товаров с первой страницы) на страницах 13-15 и т.п.
      Глубокую фильтрацию я еще 3 года назад перестал использовать - это было психоделическое нечто, работающее неизвестно как. Самый распространенный баг того времени - при последовательной отмене фильтров снизу вверх... ВНЕЗАПНО! получаем мЕньшее кол-во результатов, а то и вообще эпичный 0.

      Но говно, к тому же мертвое - это jQuery и PHP. Не перепутайте!

      P.S.: Возможно фильтрация сейчас уже работает нормально (использую только несколько верхних уровней), но со сломанной пагинацией сталкивался в прошлом году пару раз.


  1. Dadadam999
    11.01.2025 17:21

    Фреймворки также стирают границу между фронтендом и бэкендом.

    И это ужасно, потому что проекты превращаются в одну большую кашу без разделения ответственности. Да и при разделении бэкенда и фронтенда, мы можем менять ЯП. Не нравится PHP на бэке, можно переписать всё на Go, ASP, C++, Python, Node и т.д. Не нравится React на фронте, можно заменить хоть ванильным js.

    Теперь один разработчик может реализовать функциональность "от и до": от запросов к базе данных до UI-интеракций.

    Если бэкенд и фронтенд пишутся на одном ЯП, это не значит, что у них одинаковая специфика. Только знание ЯП не даёт понимание всех сфер, где он применяется.

    Может показаться, что React Server Components с SQL-запросами похожи на старый добрый PHP с его смешением HTML, CSS и SQL.

    Я рад, что описанные в статье js фреймворки добрались до уровня php дястелетней давности, но в современном php давно принято использовать OOП, шаблонизаторы, подходы по разделению логики по типу MVC и т.д. Как понимаю в реакте, даже OOП без костылей использовать нельзя поэтому сравнение с PHP очень странное.

    Но главное отличие в том, что мы больше не создаем "спагетти-код".

    Ещё как создаёте и даже порой более ужасный, чем создавали php программисты 10 лет назад. Когда увидел SQL запросы, вставленные в html теги, что воспринимается, как норма, ужаснулся куда катятся фулстек решения на js.

    П.С. У js тоже есть хорошие решения, например нода для бэкенда, анагуляр для сложных и средних проектов на фронте и вуе для простых и думаю можно ещё десяток назвать хороших. Но статья - явный байт на холивар.


    1. JBFW
      11.01.2025 17:21

      Когда увидел SQL запросы, вставленные в html теги, что воспринимается, как норма, ужаснулся куда катятся фулстек решения на js.

      ну это не новое решение и не только js - тоже когда-то казалось это удобным, пока не налетел на грабли поддержки старого кода.

      Просто "милленниалы изобрели грабли" )