Если вы как я долгое время считали, что JavaScript – это такой «игрушечный» язык на котором пишут анимашки для менюшек и падающие снежинки на форумах под новый год, а потом очнулись в 2016 году с мыслями WTF: react, flux redux, webpack, babel,… не отчаивайтесь. Вы не одиноки. Материалов по современному фронтенду в сети много, даже слишком много. Под катом еще одно альтернативное мнение о том, каково это учить JavaScript в 2016 году.

Итак, нам потребуются: React, React Dev Tools, React-Hot-Loader, React-Router, Thunk, Redux, Redux Dev Tools, Semantic-UI, Webpack, Babel и npm.

На первый взгляд много. Сравним с бекендом: MVC-фреймворк, ORM, Data Mapper, IOC-контейнер, логер, профайлер, очереди, управление конфигурациями, сборка и выкладка… Список можно продолжить, но думаю идея понятна и так: с ростом сложности решаемых задач растет и сложность инструментов. Все чаще мы употребляем термин Web App вместо Web Site, акцентируя внимание на богатых возможностях современных веб-приложений.

Почему именно этот стек?


Если вдуматься и отбросить все лишнее, то единственный ресурс постиндустриальной эпохи, через который можно выразить все остальные – это время. Освоение каждой новой технологии требует затрат времени, а значит более перспективны инвестиции в технологии, которые не устареют в ближайшие пару месяцев. Дополнительный плюс получают технологии с пологой кривой обучения.

React + Redux VS Angular VS Yet Another JS Framework


Только ленивый не сравнил Angular с React’ом (приписав при этом, дескать, сравнение не корректно, Angular – фреймворк, React – библиотека). Пойдем от обратного. Почему бы не выбрать что-нибудь эдакое, типа Vue, Ember или, упаси боже, Elm?

  1. Поддержка крупных вендоров
  2. Размер сообщества

Благодаря этим факторам, вероятность выживания Angular и React выше. Простите, другие замечательные хипстерские решения, нам не по пути. Итак, почему React? Лично для меня выбор был не простым:

  1. Меня пугали jsx-файлы
  2. Я кое-что умел на Angular 1.x и переходить на другую технологию было психологически не комфортно
  3. ng2 по-умолчанию идет в комплекте с TypeScript, что мне как стороннику статической типизации ближе
  4. ng подкупал подходом «работает из коробки». Копаться в многообразии npm-пакетов решительно не хотелось.

Короче, я заставил себя изучить все статьи-сравнилки и написать Todo App на React, что склонило чашу весов в противоположную сторону. Ключевыми факторами для меня стали:

  1. HTML-шаблоны Angular — ужасны и их синтаксис меняется от версии к версии. В React шаблон – это JavaScript, потому что компонент – не более чем View. Сообщения об ошибках в React лучше.

  2. Как ни странно, TypeScript. При более детальном изучении оказалось, что не все так прекрасно. Во-первых TypeScript – это не полноценный язык со статической типизацией, а транспилер. Это сильно ограничивает возможности использования шаблонов и мета-программирования. Во-вторых, далеко не все npm-пакеты идут в комплекте с d.ts-файлами. Короче, Flow показался проще в прикручивании. В-третьих, у TypeScript есть как ярые фанаты, так и противники. Если фанаты TS сравнительно лояльны к ES6, то обратное – не верно. ES6 получает дополнительное очко к Bus Factor.
    Если вам нравится TypeScript, ничто не мешает использовать его вместе с React. Просто конкретно мне он пока не дал критического объема преимуществ, чтобы заставить тратить время на еще один элемент в стеке.
  3. Доклад Дэна Абрамова про «путешествия во времени». Если ваш опыт в бекенде похож на мой, то вы без труда увидите, что новомодный flux – это CQRS и Event Sourcing «вид в профиль». Вместо проекций – редюсеры, а вместо команд и доменных событий – экшны. А если вы работали, например, с WPF, то разобраться с React – вообще дело пары вечеров.
    Да, Redux можно использовать и с Angular, он никак не привязан к React, но для React уже есть react-redux и react-hot-loader. Наверное, для Angular тоже есть, но мейнтейнер Redux’а явно на стороне React.

Для React и Redux доступно два расширения Chrome. Рекомендую поставить оба, чтобы сделать отладку приятной.

Таким образом, связка React + Redux:
  1. Более-менее проста в изучении, потому что в основе лежит простая идея ui = f(state => props), где f — это реакт-компонент, state — redux, а state => props — это react-redux.
  2. Не тащит за собой дополнительных зависимостей
  3. Обладает лучшим на данный момент Tool Support (IDE и плагины для Chrome)

Есть еще всякие ништяки, вроде React Native, но я им не пользовался, поэтому поделиться на эту тему мне, к сожалению, нечем.

А flux и все эти модные словечки. Как это работает?


Возможно, для фронтенда flux — это некое откровение. Для бекендщика разница между CQRS и flux — не велика. React — это наше представление. Оно может зависеть от props (read-only) или state (mutable). В зависимости от state и props компонент может отображаться по-разному. Эта логика содержится в методе render. Компонент сам себя не перерисовывает. Вместо этого, он полагается на экосистему React. Мы можем либо изменить свое состояние (с помощью метода setState), либо быть перерисованными извне (переданы новые props). Обработчики событий для UI-элементов передаются через props. Получается такой код
<button onClick={this.props.handleClick} />.
Состояние приложение хранится в Redux и представляет собой json-объект. Redux следит за изменение объекта. Изменением считается изменение ссылки, поэтому вместо изменения текущего состояния необходимо конструировать новое путем копирования и модификации старого. Проще всего это сделать через spread-оператор:
const newState = {...prevState, somethingNew: 'blah-blah'}

Пакет react-redux осуществляет односторонний байндинг redux state => react component с помощью метода connect. При изменении состояния Redux сам перерисует необходимые компоненты, передав в props dispatch и часть общего state, хранимого в Redux. Какую часть состояния и какие функции на основании store.dispatch передавать — решать вам. Я рекомендую передать все обработчики событий компонента и не «светить» dispatch в компонентах.

State содержится в Redux, но и у компонентов есть свой state. Какой из них использовать?


Разработчик Redux предлагает делать как удобнее. Это не совсем формальный совет. У нас сложилась практика использовать state компонента только для форм или в целях оптимизации.

JSX


JSX – это не JavaScript. Да, React можно писать без JSX, но проще тогда без React’а. Вообще ситуация с HTML-шаблонами напоминает мне засилье шаблонизаторов для PHP лет десять назад. Самым монструозным из всех был конечно Smarty. Мне казалось, что люди сошли с ума. Как иначе можно было объяснить желание написать шаблонизатор… для шаблонизатора?

JSX – простой и понятный способ использовать JavaScript в шаблонах. Вам не нужно учить дополнительный ЯП, просто оберните теги в (скобки), а код внутри тегов {в другие скобки}. И все. Да, так просто. Если вас беспокоит разделение логики и представления, перестаньте беспокоиться прямо сейчас. React — это View-библиотека. За состояние приложения (в т.ч. поведение) отвечает Redux. React может «диспатчить» сообщения в store, а Redux будет их обрабатывать либо через редьюсеры (чистые функции), либо через специальные middleware (побочные эффекты).

React позволяет создавать functional stateless-компоненты:
const StatelessComponent = props => (<div>Hello, {props.name}!</div>)

Или компоненты-классы:
class Hello extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Функциональная запись короче и лучше читается. Но я рекомендую не увлекаться экономией строк и вместо:
export default props => (<div>{props.title}</div>)

использовать чуть менее лаконичное, но более безопасное:
const StatelessComponent = props => (<div>{props.title}</div>)
StatelessComponent.propTypes = {…}
export default StatelessComponent

Во-первых, если вы не передадите параметры, то React недвусмысленно намекнет в консоли, что вы не правы. Во-вторых, WebStorm умеет анализировать PropTypes и при авто-дополнении заботливо вставит все required props.

Babel


Если вы не поняли на каком языке примеры кода выше, не расстраивайтесь. Это не JavaScript, ну не совсем JavaScript. Это ES6 + JSX. С JSX мы разобрались в параграфе выше – это просто синтаксический сахар для шаблонизации (почти как <?=$var?> в PHP или @Model.Param в Razor).

С ES6 ситуация чуть сложнее и запутаннее. Если коротко:

  1. JavaScript собрали на коленке под нужны тогдашнего интернета, который представлял собой действительно в основном гипер-текст.
  2. Прошло некоторое время и в сайты начали пихать все что угодно, кроме текста.
  3. Язык в существующем виде перестал удовлетворять нуждам рынка. Консорциум ECMA стал придумывать всякие новые фичи и стандарты языка, да с такой скоростью, что браузеры не успели все внедрить.
  4. В сообществе психанули и написали Babel – транспилер из JavaScript в… JavaScript. Ну в смысле из «нового» JS в «старый», который браузеры поддерживают.

Babel умеет транспилить не только JS, но и JSX, что позволяет писать React-приложения на ES6.
Да, есть React.createElement. Можно писать на React-приложения и на ES5, но зачем?

Стоит отметить, что ES6 – это не истина в последней инстанции. Некоторые фичи до сих пор являются экспериментальными (например, генераторы) и для их использования потребуются полифиллы (библиотеки, реализующие экспериментальные фичи стандарта). Частично из-за этого мы решили отказаться от redux-saga в пользу redux-thunk, хотя и идея диспатчить функции до сих пор не кажется мне изящной (она просто работает).

Webpack


Так, то есть пишем мы на ES6 + JSX, а в бразуере выполняется минифицированный JS. Все это напоминает историю изобретения высокоуровневых ЯП. Люди могли писать более эффективные программы на ассемблере, но предпочли удобство и продуктивность. Раз есть исходники и компилятор (транспилятор в нашем случае), то потребуется и система сборки. Если в вашей пещере было достаточно тепло и уютно, возможно, названия grunt и gulp вам ничего не говорят. Что к лучшему. На данный момент, можно считать (слава богу), что для JS есть только один сборщик – Webpack — оставивший конкурентов позади. Можно считать, что webpack — аналог maven или msbuild (кому что ближе) в мире фронтэнда. Не смотря на то что, конфиги webpack’а на первый взгляд напоминают некромантские свитки, через какое-то время привыкаешь. Наверное, каждый любитель фронтенда должен хотя-бы раз в своей жизни написать tutorial по настройке webpack, также как каждый фанат ФП – tutorial по монадам.

Что нужно знать про webpack:
  1. Вам потребуется кто-то, кто умеет его настраивать
  2. npm start — для запуска dev-сервера
  3. npm run build для сборки фронта на продакшн

Вообще Webpack собирает не только JS, но еще и sass, svg, шрифты и вообще все что душе угодно, но я пока еще не готов написать полноценный туториал, так что поищите на просторах интернета.

Npm


У Ruby есть gem’ы, у php – composer, у .NET – nuget. Короче, JavaScript тоже потребовался пакетный менеджер. Изначально npm использовался в nodejs-разработке (отсюда и название — node package manager), а для фронта использовался bower. Потребность в последнем как-то отпала сама собой с повсеместным переходом использованием ES6, Webpack и TypeScript. Этот параграф добавлен лишь для того чтобы отметить, что npm использует файл package.json, внутри которого можно написать:

"scripts": {
    "build": "webpack --config webpack/config.js -p",
    "start": "webpack-dev-server --config webpack/config.js"
  }

Без этих строк npm run build и npm start не заработают.

Было бы логичнее вызывать npm build, а не npm run build, но эта команда зарезервирована для внутренних целей npm, так что ничего не выйдет.

React-Hot-Loader


Ключ --hot запускает dev-сервер webpack’а с «горячей заменой». Согласитесь, билдить при каждом изменении – довольно уныло. HMR (hot moudle replacement) делает это за вас. react-hot-loader позволяет избежать при этом перезагрузки страницы и потери текущего состояния. Настройка hot-loader’а правда, довольно тонкая работа и вообще фича — экспериментальная и работает не всегда. Особенно сложные отношения у горячей замены с react-router. Но к хорошему быстро привыкаешь и рано или поздно вам захочется написать if(module.hot) для того, чтобы страница не перезагружалась.

React-Router и Thunk


Основная ниша React’а в Web – это конечно SPA-сайты. А какой SPA-сайт без навигации и общения с сервером. Первую задачу решает react-router. Здесь альтернатив нет. Из неприятных сюрпризов:

  1. Четвертая версия не совместима с третьей из-за чего у нее проблемы с пакетом history.
  2. Для hot-reload нужно соблюдать некоторые нюансы, иначе в консоли будут появляется предупреждения о том, что нельзя заменить route. К счастью ошибки достаточно информативные и исправить их просто.
  3. RouterMiddleware может сломать Redux плагин при совместном использовании.
  4. Не очевидно, что вместо тегов <Route /> можно использовать JavaScript-объекты и передать их в компонент роутера <Router routes={routes} />. Это бывает полезно, при разработке модульных приложений, когда структура маршрутов не известна заранее.

Thunk – это middleware для redux, позволяющее диспатчить функции вместо объектов. Чаще всего используется для запросов к серверу. В компоненте делать запросы к серверу – не комильфо. Редьюсеры – вообще дожны быть чистыми функциями. Ничего не остается, как делегировать это middleware.

Альтернатива thunk – redux-saga. Сага предоставляет больше возможностей, но у нее довольно крутая кривая обучения, и она тащит за собой полифиллы для генераторов. При правильном проектировании логики в компонентах нет, а connect к стейту Redux в основном производится через фабричный метод. В общем, мне показалось, что нет большой разницы, как именно управление перейдет к fetch и будет ли написано yield или then. На сайте Redux пример с thunk, так что по совокупности причин сагу я отложил до лучших времен.

Semantic-UI


Раз мы заговорили про SPA-приложения, то кроме навигации и запросов к серверу нужны еще компоненты, которые будут ту самую серверную информацию отображать. Для React есть обвязки Bootstrap, Material UI, Syncfusion Web Essentials (хотя эти обвязки не честные – там внутри jQuery). Наш выбор остановился на Sematic-UI. Решение удалось принять очень быстро – сначала отмели платные компоненты. Material UI не стали использовать из-за обилия анимации (сложнее модифицировать). Остались Bootstrap и Semantic. На Бутстрапе уже пол интернета сделано и в целом, Семантик показался более визуально-привлекательным. В общем, остановились на нем. Сразу оговорюсь, что использование Semantic UI – строго опционально, потому что минифицированная версия весит около 500кб.

Так что разрабатывать фронтенд в 2016 году вполне себе комфортно. Да инструментов много, многие библиотеки не совместимы, новые версии выходят очень часто. Это разумная плата за гигантский скачок в качестве фронтенд-стека.
Поделиться с друзьями
-->

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


  1. AlexMal
    11.04.2017 03:56
    -27

    >node
    > 0.1*0.1
    0.010000000000000002
    > 0.1*0.2
    0.020000000000000004
    > 0.1*0.3
    0.03
    > 0.1*0.4
    0.04000000000000001
    > 0.1*0.5
    0.05
    > 0.1*0.6
    0.06
    > 0.1*0.7
    0.06999999999999999
    > 0.1*0.8
    0.08000000000000002
    > 0.1*0.9
    0.09000000000000001
    

    У меня все.


    1. SerafimArts
      11.04.2017 04:02
      +20

      IEEE 754


      У меня всё.


    1. TheShock
      11.04.2017 04:09
      +4

      Что все? Это IEEE 754, во всех языках, которые его используют так будет.

      А статья — очередное «вступление в мир моды на быдлокод в JS», которых, как сам автор сказал, уже очень много и потому не нужна


      1. Xandrmoro
        11.04.2017 11:09

        Вот только в нормальных языках есть decimal.


        1. rafuck
          11.04.2017 11:17
          +1

          … и long int или его аналог. В отличие от JS, в котором все, что не помещается в 4 байта преобразуется в double:

          console.log(12345678999999999-12345678999999998); // 2
          


          1. VolCh
            11.04.2017 12:23
            +1

            В JS — всё double, тут никаких int нет by design, если не считать объектные расширения


            1. rafuck
              11.04.2017 13:12

              Да, извиняюсь, был неправ. Видимые отличия от использования целочисленных типов начинаются с 54 бит.


    1. Shannon
      11.04.2017 04:22
      +2

      golang:

      package main
      import "fmt"
      
      func main() {
      	var a float32 = 0.1
      	var b float32 = 0.1
      	fmt.Println(a * b)
      	b = 0.9
      	fmt.Println(a * b)
      }
      

      > 0.010000001
      > 0.089999996
      


    1. Ogra
      11.04.2017 06:46
      +2

      Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
      
      1> 0.1 * 0.1.
      0.010000000000000002
      2> 0.1 * 0.2.
      0.020000000000000004
      3> 0.1 * 0.3.
      0.03
      4> 0.1 * 0.9.
      0.09000000000000001
      
      


    1. vintage
      11.04.2017 08:21

      D:


      import std.stdio;
      
      void main()
      {
          writeln( 0.1 + 0.2 ); // 0.3
          writeln( 0.1 + 0.2 == 0.3 ); // true
      }


      1. rafuck
        11.04.2017 11:00

        а так?

           0.1 + 0.2 == 0.3f
        


        1. rafuck
          11.04.2017 11:06

          Поздно заметил, что вы складываете. А вообще, никаких неожиданностей

          writeln(0.1*0.2 == 0.02f); //false
          


          1. vintage
            11.04.2017 11:46

            Тем не менее:


            writeln( 0.1 * 0.2 ); //0.02


            1. vintage
              11.04.2017 11:59

                  writeln( 0.1 * 0.2 == 0.02.to!double ); //true


              1. rafuck
                11.04.2017 12:57
                +4

                И все равно не понимаю, что вы пытаетесь доказать? Что операции с числами с плавающей точкой в D не соответствуют стандарту IEEE754?

                writefln("%1.18e", 0.1*0.2); //2.000000000000000042e-02
                


                1. vintage
                  11.04.2017 14:39
                  -2

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


                  1. rafuck
                    11.04.2017 14:49
                    +4

                    Но речь в этой ветке совсем не про округление при выводе.


                    1. vintage
                      11.04.2017 15:00
                      -3

                      Именно про округление, перечитайте её :-)


                      1. rafuck
                        11.04.2017 15:05
                        +4

                        Про округление при выводе здесь никто кроме вас не писал :)


                        1. vintage
                          11.04.2017 17:14
                          -3

                          Вам, конечно, виднее.


      1. Shannon
        11.04.2017 14:12
        +1

        В данном случае даже опаснее доверять выводу writeln, так как:

        import std.stdio;
        
        void main() 
        {
        	double a = 0.1;
        	double b = 0.2;
        	if(a + b == 0.3) {
        		writeln(true);
        	}
        	else {
        		writeln(false);
        	}
        }
        

        > false
        

        Да и в целом в любом языке так, все случае не предугадаете, когда будет 0.3, а когда 0.3000000001
        Поэтому в любом случае стоит делать проверку на диапазон или любой другой хак.


        1. VolCh
          11.04.2017 14:27
          +3

          Сравнение вещественных чисел через сравнение их разности с допустимой погрешностью — не хак, а стандартный инструмент решения математических задач численными методами.


    1. AlexMal
      11.04.2017 14:31
      -7

      Я просто хотел сказать, что для каких-то важных, точных вычислений, JS абсолютно не годится. Вообще, числа с плавающей запятой — это отдельная тема.


      1. rafuck
        11.04.2017 14:51
        +5

        Он не годится в той же мере, в какой не годятся вообще все вычисления в формате IEEE754 на всех платформах и ЯП.


    1. Captcha
      11.04.2017 15:08

      Дай-ка угадаю. Гуманитарий?


  1. Fedcomp
    11.04.2017 08:31

    > Первую задачу решает react-router. Здесь альтернатив нет
    react-router-redux?


    1. marshinov
      11.04.2017 08:49
      +2

      Разве эта библиотека — не построена поверх react-router? Мне казалось, что она просто дополнительно пропускает переходы через store, чтобы сохранить time travel. Нет?


      1. VasilioRuzanni
        11.04.2017 11:17
        +1

        Ну она синхронизирует текущую позицию в store и обратно — полезно для Time Travel, доступа к данным метонахождения через Redux, что позволяет компоненту в ряде случаев не зависеть от роутера. Но это совершенно точно не «альтернатива» :)

        (Более чем) реальная альтернатива react-router-у — junctions.js.


  1. redfs
    11.04.2017 08:39
    -1

    Как короткое описание своего технологического стека c возможностью изучать тему «вглубь» по ссылкам — статья понравилась.
    Немного критики. К сожалению, ряд моментов режет глаз.

    Сравним с бекендом: MVC-фреймворк, ORM, Data Mapper, IOC-контейнер...
    Почему MVC? Почему фреймворк? Почему ORM? И т.д. Это вовсе не необходимые компоненты бэкенда. Я бы назвал их «модными», но бэкенд может быть построен на совершенно других принципах. Кмк, такое сравнение тут не совсем к месту.
    Самым монструозным из всех был конечно Smarty. Мне казалось, что люди сошли с ума. Как иначе можно было объяснить желание написать шаблонизатор… для шаблонизатора Perl?
    Конечно же автор имеет в виду php, а не perl. Фраза о «шаблонизаторе для шаблонизатора» тоже модная, но слишком часто используется не в тему.


    1. Ogra
      11.04.2017 08:48
      +2

      Автор действительно имеет в виду Perl — первая версия PHP была написана на Perl и под его сильным влиянием. $ перед каждой переменной? Это из Perl!


      1. redfs
        11.04.2017 09:02

        первая версия PHP была написана на Perl
        Это шутка что ли или я туплю? Насколько мне помнится, php/fi сразу был написан на C.
        Исправление… Понял о чем вы, о первом наборе скриптов на perl.


    1. marshinov
      11.04.2017 08:53

      Почему MVC? Почему фреймворк? Почему ORM? И т.д. Это вовсе не необходимые компоненты бэкенда. Я бы назвал их «модными», но бэкенд может быть построен на совершенно других принципах. Кмк, такое сравнение тут не совсем к месту.

      Потому что примерно 90% кода из мира веб-разработки, который я видел или поддерживал — это MVC-фрейворк + ORM и не более. На PHP — это чаще всего Active Record, на Java — Hibernate, на .NET — Entity Framework. Да, бывают другие стеки. Но мейнстрим — именно ORM + MVC = love


  1. VolCh
    11.04.2017 09:13

    Как по мне, то для разработчика MV* ООП веб-бэкендов, не стоит начинать свой путь во фронте с React+Redux, достаточно одного React, или React+MobX, если практикуешь DDD. Redux — это полное разделение состояния и поведения, а не их инкапсуляция в объектах.


    1. marshinov
      11.04.2017 09:28
      +1

      Action'ы redux'а — это же не доменные объекты, а скорее просто message. Что мешает ловить экшны, создающие эффекты в middleware, обрабатывать в любом удобном виде, в т.ч. с применением DDD и по завершению операции выбрасывать DomainEventSucceeded / Failed и уже их обрабатывать в редюсерах?


      1. VolCh
        11.04.2017 09:50

        Доменные объекты инкапсулируют данные и поведение. Redux же их разделят по крайней мере на уровне рекомендуемых практик. Может и можно технически хранить в store полноценные объекты с методами, реализующими доменное поведение, а потом дергать их в редюсерах, но точно это будет шоком для подавляющего большинства активно использующих Redux. А скорее всего эта возможность либо просто заблокирована (может только в дев-режиме), либо ожидаемой реакции на вызов методов объекта не будет, поскольку redux+react не заметят, что изменилось состояние объекта в редьюсере, поскольку ожидает либо возврат того же объекта без изменений, либо нового объекта. В общем не прокатит что-то вроде (state, value) => state.domainObject.setValue(value).


        Можно отдельно вынести мутабельную модель и проецировать её состояние на redux-стор в миддлварах, но что нам это даст, кроме "потому что можем"?


        1. VasilioRuzanni
          11.04.2017 11:41
          +1

          А кто мешает применять DDD? Просто данные будут в сторе, а логика — отдельно — в функциях, сагах, и всяких таких штуках. Разделение ответственности же, до сих пор же идут споры о «правильном DDD» — никто не говорит, что для этого нужен один мутабельный объект модели для каждой сущности с методами поведения. Концептуально DDD никто не мешает использовать.

          P.S. На практике немного сложнее, в сторе обычно находятся не только данные «domain model», а куча разного рода другого состояния приложения.


          1. VolCh
            11.04.2017 12:39

            По-моему сложно будет говорить о выделенном слое бизнес-логики, всё будет в одной куче и бизнес, и инфраструктура, и UI.


            1. VasilioRuzanni
              11.04.2017 12:50
              +1

              Ну, этот паттерн, который называют то «saga», то «process manager» — как раз об этом. Он описывает чистую логику, результатом которой являются те или иные «action»-ы. Тем самым хранилище состояния (Redux) только тупо хранит состояния и меняет его с помощью редьюсеров, а сама логика обрабатывается специализированными функциями.

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


        1. mkusher
          11.04.2017 23:52

          state у редакса это готовое отображение мира для UI. О том, что состояние надо поменять как раз и говорят доменные объекты, которые умеют делать dispatch. Это в общем-то типичный CQRS+ES


          1. vintage
            12.04.2017 07:34
            +1

            DOM — это готовое отоброажение мира для UI. А state у вас — это промежуточная модель.


            1. mkusher
              12.04.2017 14:10

              тогда замените UI в моем сообщении на DOM


              1. vintage
                12.04.2017 19:05

                А зачем? Может лучше state заменить на DOM?


                1. mkusher
                  12.04.2017 19:40

                  у redux'a нет DOM, либо я вас не понял


                  1. vintage
                    12.04.2017 20:51

                    Вот именно. Зачем дому редукс?


                    1. VolCh
                      13.04.2017 09:04

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


                      1. vintage
                        13.04.2017 09:11

                        А зачем дому реакт? :-D


                        1. VolCh
                          13.04.2017 09:42

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


                          1. vintage
                            13.04.2017 10:00

                            Это какие плюсы и минусы вы имеете ввиду?


                            1. VolCh
                              13.04.2017 18:12
                              +2

                              Плюсы, навскидку:


                              • декларативное описание целевого состояния DOM без учёта текущего на языке очень близком к HTML с возможностью использовать все средства JS
                              • средства автоматического обновления DOM при изменении состояния
                              • компонентная модель с высоким уровне изоляции
                              • исключительно односторонний биндинг
                              • довольно заметные оптимизации ререндеринга DOM
                              • минимум три способа создавать компоненты (обычные функции, наследники Component, наследники PureComponent)
                                Минусы:
                              • нестандартный синтаксис, требующий компиляции в JS и изучения
                              • невозможность создавать компоненты, представимые в DOM типами, отличными от элементов (прежде всего напрягает отсутствие возможности представить компонент списком элементов)
                              • местами многословный синтаксис
                              • неопределенность в плане лучших практик
                              • доминирование отдельных не самых лучших в общем случае решений


                              1. vintage
                                13.04.2017 21:01

                                декларативное описание… все средства JS

                                Что-то тут не так.


                                исключительно односторонний биндинг

                                Что плохого в двустороннем?


                                минимум три способа создавать компоненты

                                То есть чем больше способов создавать компоненты — тем лучше?


                                1. VolCh
                                  14.04.2017 13:08

                                  Что-то тут не так.

                                  Всё так. Интерфейс сугубо декларативный, а логика вполне может быть императивной. Собственно все (ну или почти все, всякие ПЛИС не рассматриваем) так называемые декларативные средства современного ИТ сводятся к императивным командам тьюринг-подобных процессоров.


                                  Что плохого в двустороннем?

                                  Конфликты и циклы. Да даже без них — усложнение логики в виду отсутствия единого источника правды.


                                  То есть чем больше способов создавать компоненты — тем лучше?

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


                                  1. vintage
                                    14.04.2017 13:26

                                    Всё так. Интерфейс сугубо декларативный, а логика вполне может быть императивной.

                                    setState — какой-то совсем не декларативный интерфейс.


                                    Конфликты и циклы. Да даже без них — усложнение логики в виду отсутствия единого источника правды.

                                    Это всё особенности Ангуляра к двусторонним биндингам, в общем случае, отношения не имеющие.


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

                                    Например? Что за условия могут потребовать создавать компоненты по разному?


                                    1. raveclassic
                                      14.04.2017 13:32
                                      +1

                                      setState — какой-то совсем не декларативный интерфейс.
                                      Ну вот зачем придираетесь? Под «всеми средствами» JS, очевидно, имелся полный доступ к отрисовываемым объектам, фильры, сортировки, мерджи и т.п. при непосредственном рендеринге.

                                      Это всё особенности Ангуляра к двусторонним биндингам, в общем случае, отношения не имеющие.
                                      WPF и компания, видимо, в общем случае тоже к двусторонним бингингам отношения не имеют? Там это давно наболевшая проблема.

                                      Например? Что за условия могут потребовать создавать компоненты по разному?
                                      stateless vs stateful


                                      1. vintage
                                        14.04.2017 15:43

                                        Под «всеми средствами» JS, очевидно, имелся полный доступ к отрисовываемым объектам, фильры, сортировки, мерджи и т.п. при непосредственном рендеринге.

                                        Например, взять и добавить атрибут в уже отрисованный дом?


                                        Там это давно наболевшая проблема.

                                        В $mol такой проблемы нет, а двусторонние биндинги есть. Что я делаю не так?


                                        stateless vs stateful

                                        И зачем их по разному создавать?


                                        1. VolCh
                                          14.04.2017 15:49

                                          И зачем их по разному создавать?

                                          Уменьшение накладных расходов


                                          1. vintage
                                            14.04.2017 15:53

                                            За счёт чего?


                                    1. VolCh
                                      14.04.2017 14:40

                                      setState — какой-то совсем не декларативный интерфейс.

                                      Он не относится к описанию целевого состояния DOM. Целевой DOM является чистой функцией от props и state.


                                      Это всё особенности Ангуляра к двусторонним биндингам, в общем случае, отношения не имеющие.

                                      Это общая проблема двусторонних биндингов. В Excel/Calc попробуйте двумя ячейкам друг на друга сослаться. А лучше через десяток промежуточных.


                                      Например? Что за условия могут потребовать создавать компоненты по разному?

                                      • stateless компонент (чистая функция от свойств)
                                      • stateful компонент с "нативным" setState
                                      • stateful компонент со сторонним хранением и управлением состояния (redux, mobx, rxjs и т. п.)


                                      1. vintage
                                        14.04.2017 15:53
                                        -1

                                        Он не относится к описанию целевого состояния DOM. Целевой DOM является чистой функцией от props и state.

                                        Зачем тут чистота, если достаточно идемпотентности?


                                        Это общая проблема двусторонних биндингов.

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


                                        В Excel/Calc попробуйте двумя ячейкам друг на друга сослаться. А лучше через десяток промежуточных.

                                        Эксель ругается на циклические зависимости.


                                        stateless компонент (чистая функция от свойств)
                                        stateful компонент с "нативным" setState
                                        stateful компонент со сторонним хранением и управлением состояния (redux, mobx, rxjs и т. п.)

                                        И зачем тут 3 разных способа создания компонента?


                                        1. VolCh
                                          15.04.2017 08:45

                                          Чистота чище :)


                                          Именно, ругается, но позволяет их создавать.


                                          Чтобы уменьшить накладные расходы. Не нужно состояние — не нужно его и вводить в компонент. Нужная частичная поддержка состояния — минимальное количество поддерживающего кода, полное — максимальное.


                                          1. vintage
                                            15.04.2017 09:10
                                            +1

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


                                            Не позволяет. Формулы с циклическими зависимостями не работоают.


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


                                            1. VolCh
                                              15.04.2017 13:49

                                              Тут я не о сторах, а о целевом состоянии DOM — оно чистая функция от состояния.


                                              Позволяет. Не работает, но позволяет, а ошибка в рантайме выводится.


                                              Не определять состояние в компоненте не значит что для него не будет создана инфраструктура его хранения и управления. Она будет создана просто будет простаивать. А хочется гибкости, чтобы ресурсы тратились сообразно реальному использованию, а не всем гипотетическим сценариям.


                                              1. vintage
                                                15.04.2017 16:18
                                                +1

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


                                                Позволяет ввести любую формулу. Но биндинга никакого по ней не создаёт.


                                                Что за "инфраструктура"? Объект со ссылкой на прототип? Ну офигеть инфраструктура.


                                                1. VolCh
                                                  17.04.2017 12:07

                                                  Чем они чудовищные? Очень легко поддерживаются. И позволяют сделать реальное обновление парой свойств.


                                                  Формулу сохраняет — значит биндинг создаётся. Просто он не работает. Хорошо, что ошибку выводит, а не в бесконечную рекурсию уходит.


                                                  Объект с ненужными свойствами. Ну и да, разрешение методов происходит, что небесплатно.


                                                  1. vintage
                                                    17.04.2017 17:04
                                                    +1

                                                    Например, такие: https://habrahabr.ru/post/326484/#comment_10174502


                                                    Формула — это просто текст. Она сохраняется, если соответствует формальной грамматике выражений. Следующий шаг — вычисление и трекинг зависимостей — уже не проходит и падает с ошибкой.


                                                    Любое замыкание — это объект, хранящий ссылку на функцию и контекст её создания, хранящий "ненужные переменные". Проще говоря — разницы нет от слова "вообще".


          1. VolCh
            12.04.2017 09:52
            +1

            Что за доменные объекты, которые знаю о dispatch в частности и о нуждах UI вообще?


            1. mkusher
              12.04.2017 14:09

              про нужды UI знает reducer(в терминах cqrs+es это был бы проектор), доменный объект знает только о том, что что-то произошло


              1. VolCh
                12.04.2017 14:31

                Объект знает, что с ним произошло и даже может сообщить кому-то если спросят. Как об этом должен узнать redux, чтобы вычитать изменения в графе домена, создать соответствующие action и диспетчеризировать их?


                1. VasilioRuzanni
                  12.04.2017 14:43

                  Redux вообще не должен ничего «знать, чтобы диспетчеризовать». Его задача — только хранить состояние. У вас должен быть отдельный кусок программы, ответственный за перехват тех или иных action'ов и за то, чтобы на это как-то отреагировать (в том числе взять текущий стейт, проанализировать его и, вероятно, запустить какие то экшены).

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


                  1. VolCh
                    12.04.2017 14:58

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


                    1. VasilioRuzanni
                      12.04.2017 15:03

                      Ну, скажем так, его задача наметить паттерн для этого (dispatch, actions, middlewares, reducers), но не обязательно предлагать непосредственный механизм для этого (вместо него — предлагается подключать что-то свое как middleware).

                      UPDATE: Не то, чтобы это хорошо или плохо — с одной стороны, это дает больше гибкости, но с другой — все делают кто во что горазд :) Особенно когда сложность приложения выходит за пределы разумности применения простых вещей вроде redux-thunk (который, вроде как стартовая точка и он же — первый middleware, с которым все сталкиваются).


                1. mkusher
                  12.04.2017 14:54

                  очевидно объект сообщит redux'у :)


      1. vintage
        11.04.2017 09:51

        Мешает экпоненциальная костыльность "современной front end разработки" :-)


  1. SergeyVoyteshonok
    11.04.2017 10:13
    +2

    Почем нет ни слова про mobX? Мы же в 2017


  1. novoxudonoser
    11.04.2017 10:25

    Где relay?


  1. vintage
    11.04.2017 10:30
    +1

    Про reselect забыли!


  1. andreymal
    11.04.2017 11:04
    +6

    Для себя как бэкенд-разработчика я не увидел ответа на один очень важный вопрос: зачем это всё? Почему вдруг стало нельзя просто рисовать HTML на бэкенде и отдавать его браузеру как есть (с AJAX или без)?


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


    1. Quilin
      11.04.2017 11:39
      +5

      Как бывший фронтенд разработчик, который ныне сидит на бэке, отвечаю =)
      1. Рисовать HTML на бэкенде все еще можно, но такой подход не умеет решать ряд важных проблем. Например, если юзер взаимодействует с элементом, который меняет контент во многих разных частях приложения, скажем, отредактировал свое имя, которое должно успешно замениться повсюду на странице.
      2. Для отрисовки на бэке тратятся ресурсы networking'а, и это имело бы смысл, будь генерация HTML'а чем-то сложным и непроизводительным, но это совсем не так — любой современный шаблонизатор работает с ast, и на клиенте это все будет работать во многих случаях быстрее чем один только пинг до сервера.
      3. Отрисовка кусмана HTML'а на клиенте — это более трудозатратный процесс, чем использование DOM API, как раз потому, что снова выделяются ресурсы на парсинг и валидацию. Современные фреймворки/либы стараются использовать virtual dom, чтобы по возможности оптимизировать эти процессы.
      4. Еще, конечно, обилие мобильных клиентов требует уменьшения трафика. Готовый HTML практически всегда будет весить больше чем JSON API.

      Сайтов, которым нужны сложные взаимодействия на клиенте, точно не по пальцам пересчитать. Прикиньте, сколько в мире есть CRMок, облачных приложений, прибавьте сюда практически весь b2b-стек — почти каждая такая система выглядит и работает лучше, когда часть нагрузок передана на клиент.


      1. andreymal
        11.04.2017 11:52

        должно успешно замениться повсюду на странице

        После изменения имени страница классического сайта просто перезагрузится :)


        на бэке тратятся ресурсы

        Ни разу не сталкивался с тем, чтобы это становилось узким местом. Да и рисование HTML на бэкенде многими крупнейшими сайтами тоже тому пример. А вот то, что производительности браузера не хватает джаваскрипту — это постоянно, особенно на мобилках. Ну и gzip никто не отменял


        Отрисовка кусмана HTML'а на клиенте — это более трудозатратный процесс

        Видимо, разница в пределах погрешности, потому что видимой на глаз разницы в производительности между сайтами, активно юзающими innerHTML (PJAX/Turbolinks какие-нибудь), и сайтами на React не замечал (и на мобилках тормозят примерно одинаково).


        Прикиньте, сколько в мире есть CRMок, облачных приложений

        Прикиньте, сколько в мире есть сайтов-визиток, блогов, форумов, которые кроме гифок статичны чуть более чем полностью — но при этом в них в последнее время зачем-то пихают реакты и прочие ангуляры с js-бандлами в десяток-другой мегабайт :)


        1. Quilin
          11.04.2017 16:47
          +5

          После изменения имени страница классического сайта просто перезагрузится :)
          Заставив нас ждать пинга сервер-клиента, рендера на сервере, рендера на клиенте, репейнта… А в нашем случае, может быть, надо было только два слова всего поменять. Конечно, раз на раз тут не приходится, но сценарий, когда действие пользователя меняет несколько достаточно удаленных друг от друга частей сайта, но незначительно — в моей практике встречался слишком часто.

          на бэке тратятся ресурсы
          Вы вырвали цитату из контекста. Привожу полную цитату:
          Для отрисовки на бэке[ПАУЗА] тратятся ресурсы networking'а

          Ресурсы бэка, конечно, не особо тратятся (зависит от). Но вот когда сервер в Лондоне, а юзер в Монголии…

          видимой на глаз разницы в производительности [...] не замечал
          Зависит от сайта, разумеется. Мне доводилось замечать такую разницу на всякого рода b2b-ресурсах.

          (Не считая случаев, когда сайт таким образом написать в принципе невозможно и нужен интерактив на JS, но ведь такие сайты по пальцам можно пересчитать)
          Прикиньте, сколько в мире есть сайтов-визиток, блогов, форумов, которые кроме гифок статичны чуть более чем полностью
          То, что в мире много сайтов-визиток с морем статики, никак не подтверждает аргумент, что других сайтов — по пальцам пересчитать.

          Я лично считаю, что серебряной пули нет. Реакт не панацея (и мне лично вообще Vue больше нравится например), и толкать его повсюду — это та же мода на сложение чисел с помощью jquery только в профиль. Если ваш фронт, которому надо сделать лендинг для разовой акции, тянет туда реакт, возможно, он не очень компетентен. Если ваш бэкендер, который пилит условный trello, морщится при слове реакт и хочет все сделать на GWT/ASP.NET — про него можно сказать то же самое.


          1. andreymal
            11.04.2017 17:00
            +1

            А в нашем случае, может быть, надо было только два слова всего поменять

            Если ограничиваться только этим случаем, то получается что-то вроде document.querySelectorAll('.js-user-name').forEach(x => x.textContent = newName), и React тут как-то ни к чему)


            в моей практике встречался слишком часто.

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


            тратятся ресурсы networking'а

            В веб-приложениях всю экономию с лихвой компенсируют громадный размер js-бандлов, а при частых обновлениях кода даже браузерный кэш становится бесполезен. Зажатый gzip'ом html-код неплохо передаётся даже по EDGE (а там пинг, напомню, приближается к секунде), так что не вижу смысла экономить на спичках


            1. VasilioRuzanni
              11.04.2017 17:13
              +2

              Ну, вы как-то даже и не хотите видеть каких-то преимуществ SPA-приложений (а люди с вами зачем-то спорят). Если вам хватает ваших инструментов — у вас же их никто не отбирает. У всех задачи опять же разные. Всякие чисто «хайповые» штуки со временем уходят или находят свою нишу, а вот полезные паттерны — остаются. Неужели вы думаете, что все и каждый вокруг вас — хипстер, которому нравятся модные словечки и использует такой подход только из за своей хипстеровости (есть такое слово?) :)

              Возьмем тот же Реакт и его серверный рендеринг — и вот вам вполне себе альтернативный вариант написать чисто серверный UI (можете даже не подрубать React на клиенте). Кому-то удобнее, кому-то нет, но это еще один полезный паттерн.

              document.querySelectorAll('.js-user-name').forEach(x => x.textContent = newName)
              

              Ну очень уж притянутый за уши пример. Вывести какой-то текст во все элементы с таким-то классом — элементарно. Во фронтенде реально сложно — хранить и управлять состоянием. Рендерить — чем угодно и как угодно — просто.


              1. VolCh
                11.04.2017 17:19

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


                1. VasilioRuzanni
                  11.04.2017 17:40

                  Ну, я именно это имел в виду под «хранением и управлением состояния». Надо было добавить «и корректному отображению всего этого на UI». И тут становится понятно, что и virtual DOM не просто так придумали и всякие другие штуки для этого — чтобы была возможна концепция «UI — это функция от состояния». Но автор комментариев выше настаивает на том, что все это не нужно, и я, наравне со многими другими, с этим несогласен :)


            1. vintage
              11.04.2017 17:22

              Если ограничиваться только этим случаем, то получается что-то вроде document.querySelectorAll('.js-user-name').forEach(x => x.textContent = newName), и React тут как-то ни к чему)

              А где-то в сторонке у вас на некоторых страницах список пользователей отсортированный по имени. Ваши действия?


              1. andreymal
                11.04.2017 17:32

                на некоторых страницах

                А вот тут всплывает другая проблема: я как пользователь хочу, чтобы контент на страницах, которые я не трогаю, не менялся, и если что-то там пересортируется само по себе без явного нажатия мной F5, я сильно расстроюсь. А если на текущей странице нужно изменить что-то большее чем пару лейблов, то всё ещё нетрудно просто перезагрузить её (почти) целиком, «ресурсы networking'а» в 2017 году уже не проблема. Так что по моему личному мнению никаких действий предпринимать не нужно :)


                1. TheShock
                  11.04.2017 17:33

                  Вы своим кодом как раз поменяли все имена пользователей в той таблице.


                1. vintage
                  11.04.2017 17:34

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


                  1. andreymal
                    11.04.2017 17:36

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


                    1. VasilioRuzanni
                      11.04.2017 17:46

                      Страница может быть какая угодно. Вопрос в том, какая именно, и какой уровень отзывчивости и интерактивности ваши пользователи ожидают. Оба подхода имеют право на жизнь, очень зависит от конкретного сайта/приложения и того, какие действия с его помощью производят ваши пользователи. Просто не надо все под одну гребенку и говорить, что перезагрузка страницы — единственный тру-способ работы с веб-приложениями.


                      1. andreymal
                        11.04.2017 17:57

                        Как я отметил ещё в скобках в своём самом первом комментарии, сайтов, на которых без интерактивщины с js вообще никак (тот же условный trello), я не касаюсь. А вот для большинства других ситуаций перезагрузка страницы таки является достаточно быстрым и достаточно интерактивным способом обновления контента. И я не понимаю, зачем жертвовать примитивностью и понятностью фронтенда ради экономии сотни миллисекунд и пары килобайт трафика. (И не забываем про PJAX.)


                    1. vintage
                      11.04.2017 17:57

                      В шапке на всех страницах сайта отображается имя пользователя. Тут же можно его поменять (почему бы и нет?). В теле страницы "обсуждаем завтрашнюю стрелку" выводится список список "ребят с вашего двора", отсортированный по заданному пользователем критерию (имя, возраст, репутация, сила). В случае, если пользователь изменил имя, и на экран сейчас выводится список пользователей (а он может быть скрыт) и этот список отсортирован по имени пользователей, то в этом и только в этом случае нужно найти строку с текущим пользователем и переместить её в правильное место.


                      1. andreymal
                        11.04.2017 18:12

                        Это дело всё ещё нетрудно перезагрузить. Всю страницу или только список подгрузить через AJAX — неважно, но я не вижу смысла как-то ещё усложнять фронтенд в данном случае. Даже если рассматривать экономию трафика и времени на парсинг html, она получается микроскопическая (даже если список большой — напомню, 2017 год на дворе) и, имхо, не стоит того. А анимации не нужны (если только так не скажет заказчик, с которым спорить себе невыгодно — но это уже совсем другая история)


                        1. VolCh
                          11.04.2017 18:14

                          Давайте усложнять бэкенд? :)


                          1. andreymal
                            11.04.2017 18:17

                            Здесь я отвечаю «давайте, потому что хороший сайт должен уметь работать без js» и меня закидывают помидорами и яйцами.)


                            1. gearbox
                              13.04.2017 23:31

                              Да вы просто о разных вещах говорите, мужики. andreymal говорит о документах, VolCh vintage и прочие — о приложении. Общего у них — только броузер и html. Ваш кэп.


                              1. vintage
                                14.04.2017 00:10
                                +1

                                Давно не видел в вебе документов.


                                1. gearbox
                                  14.04.2017 10:20
                                  -1

                                  А я вот утконосов давно не видел. Документация, статейники, форумы (да, интерактивные, но все же документы, так как предоставляют информацию, в отличие от приложений, который суть провайдеры сервиса)


                                  1. vintage
                                    14.04.2017 10:22
                                    +1

                                    Приложения тоже по большей части предоставляют информацию. Всё отличие — в интерактивности.


                        1. vintage
                          11.04.2017 18:20

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


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


                  1. andreymal
                    11.04.2017 17:41

                    А перелёт с анимацией очень спорная фича, сильно не люблю когда что-то куда-то скачет (и ещё больше не люблю, когда перед выполнением какого-то действия нужно дождаться, пока это что-то перестанет скакать). А если в обсуждаемом списке пользователей будет ещё и пагинация, то вообще караул тотальное неюзерфрендли :\


      1. Xandrmoro
        11.04.2017 18:25

        > Прикиньте, сколько в мире есть CRMок, облачных приложений, прибавьте сюда практически весь b2b-стек
        Зачем здравомыслящему человеку перекладывать это всё с надёжных технологий на значительно менее надёжные?


        1. VolCh
          11.04.2017 18:30
          +1

          Вы сейчас какую технологию ненадежной назвали?


          1. raveclassic
            11.04.2017 18:39
            +4

            этот их жабаскрипт вестимо


            1. vintage
              11.04.2017 20:40

              И что у него не так с надёжностью?


              1. raveclassic
                11.04.2017 22:04
                +1

                Тут где-то меткую фразу видел: «у вас сарказм детектор барахлит» :)


    1. Compadre
      11.04.2017 11:44

      Rest позволяет унифицировать апишку для любого типа клиентов.


      1. andreymal
        11.04.2017 11:52

        Ну против API я ничего не имею


    1. novoxudonoser
      11.04.2017 11:57

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


      1. andreymal
        11.04.2017 11:58
        +1

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


        1. VolCh
          11.04.2017 12:41

          А новые фреймворки не для сайтов прежде всего, а для приложений.


          1. andreymal
            11.04.2017 12:45
            +1

            А пихают почему-то и на сайты тоже, меня вот это в первую очередь беспокоит (сама суть приложений беспокоит тоже, но ныть по этому поводу смысла пока нет)


            1. VolCh
              11.04.2017 18:43

              Если их пихают на сайты, то можете обновлять страницу — для сайтов сервер сайд рендеринг маст хэв.


              1. vintage
                11.04.2017 20:42

                Да ладно, любой сайт с богатым клиентом гораздо удобнее.


    1. marshinov
      11.04.2017 12:01

      Мы научили фронтов верстать сразу в реакт-компоненты. Это избавляет от этапа интеграции с серверной шаблонизацией, например. Можно отдельно делать фронт, отдельно бек и не разворачивать у фронта бд и все что там еще нужно. Большие возможности интерактивного UI. Думаю, это основные плюсы.


    1. VolCh
      11.04.2017 13:02
      +3

      Современные веб-приложения (а не сайты) стремятся к тому, чтобы запросов к серверу было как можно меньше и при этом в ответах отдавалось только то, что ещё не отдавалось. Минимизация количество запросов и их объёма.


      Если знакомы с классическими клиент-серверными приложениями с десктопным клиентом, то у вас же не вызывает вопроса почему сервер не даёт клиенту сразу команды GDI (вроде так в винде это называется), рисующие данніе, а даёт только данные? Современные веб-приложения по сути являются полнофункциональным десктопным клиентом, не требующего установки и запускающегося с сетевого диска. Сначала ждём пока бинарник загрузится по сети, а потом общаемся с сервером только данными, делая всю работу UI на клиенте. А серверу всё равно, обычное десктоп приложение к нему стучится за данными, мобильное, веб, а может вообще другой сервер или CLI-утилита.


  1. Labadabadubdub
    11.04.2017 14:55
    +2

    Вот эти бородатые хипстеры, диалог и кнопка «Стать модным» — это ведь стёб, я правильно понимаю?


  1. unel
    11.04.2017 18:23

    Как ни странно, TypeScript. При более детальном изучении оказалось, что не все так прекрасно. Во-первых TypeScript – это не полноценный язык со статической типизацией, а транспилер.

    Т.е. в случае с flow / babel / jsx вас это не смутило, а в случае с TypeScript — смутило?


    Это сильно ограничивает возможности использования шаблонов и мета-программирования.

    Не очень понял, какие? Можно хотя бы пару примеров?


    Во-вторых, далеко не все npm-пакеты идут в комплекте с d.ts-файлами.

    А аннотацию flow конечно же все npm-пакеты поддерживают?


    Короче, Flow показался проще в прикручивании.

    Для Flow надо прикрутить babel + babel-flow, а для TypeScript — только typescript… но это оказалось сложней?


  1. ajaxtelamonid
    11.04.2017 20:51
    +4

    Опять бедных бэкендеров мучают redux-ом. Бэкендеры, не слушайте их, берите mobx для управления состоянием, иначе со временем проклянёте тот день, когда решили разобраться с фронтом. И так проклянёте, конечно, но с mobx у вас хотя бы будет на руках работающее приложение и гораздо меньше седых волос.


    1. marshinov
      12.04.2017 12:38
      -1

      А что именно не нравится в редаксе? И в чем принципиальное преимущество mobx? Я когда сравнивал просто не нашел киллер-фич каких-то.


      1. VolCh
        12.04.2017 12:45

        Как минимум, MobX нормально оперирует привычными многим бэкендерам полноценными объектами, инкапсулирующими данные и поведение. В Redux же, по сути, есть только структуры данных типа сишных структур или паскалевских записей и функции, связанные со структурами только в голове у разработчика. Глядя на функцию нельзя однозначно сказать на работу с какой частью стора она заточена.


        1. marshinov
          12.04.2017 12:49

          Вообще-то можно. Для этого как раз и используется combineReducers. Это вопрос компоновки приложения, а не redux'а.


          1. VolCh
            12.04.2017 14:03

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


            1. raveclassic
              12.04.2017 14:08

              Эмм… зачем вам в редьюсере знать свой полный путь от корня стейта? Редьюсер должен оперировать только тем стейтом, который приходит в него первым аргументом.


              1. VolCh
                12.04.2017 14:39

                Чтобы понимать, что за стейт в него приходит.


                1. VasilioRuzanni
                  12.04.2017 14:44

                  Это не редьюсер решает, а как раз тот самый «код композиции».


                1. raveclassic
                  12.04.2017 14:47

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

                  type TUser = {
                  	id: number,
                  	name: string
                  };
                  type TAuthState = {
                  	user?: TUser,
                  	loggedIn: boolean
                  };
                  const initial: TAuthState = {
                  	loggedIn: false
                  };
                  type TLoginAction = {
                  	type: 'AUTH_LOGIN',
                  	payload: {
                  		user: TUser
                  	}
                  };
                  type TLogoutAction = {
                  	type: 'AUTH_LOGOUT'
                  };
                  type TAction = TLoginAction | TLogoutAction;
                  
                  const auth = (state: TAuthState = initial, action: TAction): TAuthState => {
                  	switch (action.type) {
                  		case 'AUTH_LOGIN': {
                  			return {
                  				...state,
                  				user: action.payload.user,
                  				loggedIn: true
                  			};
                  		}
                  		case 'AUTH_LOGOUT': {
                  			return {
                  				...state,
                  				user: undefined,
                  				loggedIn: false
                  			};
                  		}
                  		default: {
                  			return state;
                  		}
                  	}
                  };
                  
                  //compose
                  type TAppState = {
                  	auth: TAuthState
                  };
                  const app: TAppState = combineReducers({
                  	auth
                  });
                  


                  Редьюсер auth не знает ровным счетом ничего о том, где он находится в глобальном стейте app, да и не должен.


                  1. raveclassic
                    12.04.2017 14:52

                    Упс, с типами напутал:

                    const app = combineReducers<TAppState>({
                    	auth
                    });
                    

                    Но суть не меняется.


                  1. VolCh
                    12.04.2017 15:11

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


                    1. raveclassic
                      12.04.2017 15:18
                      +4

                      Значит вы не понимаете концепцию redux.

                      Вот пишете, недостаток. А какой в этом недостаток?
                      В редьюсер приходит не «какой-то стейт», а тот, с которым он умеет работать. И ему абсолютно по барабану, в корне этот стейт лежит, на 20 уровне вложенности, на полке или в холодильнике. Совершенно все-равно, все что он делает — это применяет нужный экшен к своему стейту.

                      Можете привести пример, когда вам нужно «знать, откуда пришел стейт»?


                      1. VolCh
                        12.04.2017 15:40

                        Он не знает его это стейт или не его. Вот в вашем примере придёт


                        {
                            id: number,
                            name: string
                        };

                        но это может оказаться не пользователь, а клиент.


                        1. raveclassic
                          12.04.2017 16:04

                          Так а редьюсеру-то какая разница? Если вам нужна разная обработка в зависимости от типа — заводите либо разные экшены, либо разные редьюсеры, либо добавляйте в структуру ее тип.

                          Тут есть небольшой трюк — представьте, что редьюсер — это не функция, которую где-то кто-то как-то вызывает, и может вызвать неправильно. Представьте, что это значение, которое меняется со временем при обработке приходящих экшенов. Ну, вы прямом смысле значение, объект. И из этих объектов вы собираете конечный объект стейта. Вот она функциональная композиция, вам не нужно вызывать редьюсер императивно (в большинстве случаев), достаточно обычной композиции в объект через combineReducers.

                          Из этого органично следует вывод, что редьюсер не может принять «не свой стейт», он даже думать не должен, «его это стейт или нет». Он сам «определяет собой» этот стейт.


                          1. VolCh
                            12.04.2017 16:36

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


                            1. raveclassic
                              12.04.2017 16:45
                              +1

                              Ну так изменения должны вноситься на основании сущности, которая этим редьюсером обрабатывается. Все. Редьюсер — не зависит от окружения, какая разница, клиенты или пользователи лежат в стейте, если у них структура одинаковая?

                              Если вам нужно один и тот же редьюсер использовать для хранения и пользователей, и клиентов (что, хм, странно), вы при композиции кладете его под разными ключами. Если вам нужна разная обработка, вы делаете 2 разных редьюсера (что уже ближе к правде), которые реагируют на разные и/или пересекающиеся экшены по-разному и работают с разными структурами.

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


                              1. VolCh
                                12.04.2017 16:59
                                -1

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


                                1. raveclassic
                                  12.04.2017 17:12

                                  взял и использовал один редьюсер в разных ветках стора
                                  Но зачем, если он обрабатывает одни и те же данные? Это обычная ошибка, и к redux не имеет никакого отношения.
                                  doSomthUser
                                  Видите, вы воспринимаете редьюсер, как какой-то метод, которые что-то делает со стейтом при его императивном вызове. Это не так, редьюсер — значение, которое вы можете прочитать, и все. Это значение может как-то меняться, в зависимости от описанной реакции на какие-то экшены. Ну руками снаружи вы его изменить не должны.
                                  Если уж на то пошло, то нужно было от хранения клиентов в стейте вообще избавиться, потому как ровно такая же обработка происходит в редьюсере с пользователями. Если бы эта ошибка не была допущена, то не случилось бы и следующей с нечаянным изменением обработки клиентов при изменении обработки пользователей. Это не имеет никакого отношения к redux.


                                  1. VolCh
                                    12.04.2017 17:47

                                    И у клиентов, и у пользователей есть, например, ФИО, но остальные данные различаются. И редьюсеров у каждого штук по 10 минимум, но разные, кроме вот этого проблемного.


                                    Я его воспринимаю редьюсер как функцию от старого стейта и данных экшена с побочным эффектом в виде создания нового объекта для замены им старого стейта. Что замена происходит не непосредственно в нём — дань упрощения детектирования изменений — объекты гораздо проще сравнивать по ссылке, а не по значению.Это на основании анализа кода примеров на redux и его самого такое восприятие.


                                    1. raveclassic
                                      12.04.2017 17:56
                                      +1

                                      Почему у вас 10 редьюсеров на одну сущность вместо одного? Ну вот же ошибка. Представьте, что редьюсер — таблица в базе, вы же не заводите 10 таблиц под одну сущность?

                                      Я его воспринимаю редьюсер как функцию от старого стейта и данных экшена с побочным эффектом в виде создания нового объекта для замены им старого стейта.
                                      Это на основании анализа кода примеров на redux и его самого такое восприятие
                                      Вот и неверно. Все примеры redux, которые я видел, оперируют редьюсерами как объектами, сущностями, но никак не методами или функциями от старого стейта к новому. То, что это такая функция — детали реализации.


                  1. TheShock
                    12.04.2017 18:09

                    const app: TAppState = combineReducers({
                    	auth
                    });
                    

                    У меня есть значительные замечания, но на таких примерах они проявляются слабо. Можно пример реального приложения с десятками моделей, где многие из них используются несколько раз, иногда с отличиями — ну то есть реальное приложение, а не обучающий ХеллоуВорлд? К сожалению, в интернете я таких примеров не нашел. В ООП такое вполне решается наследованием и композицией.

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


                    1. marshinov
                      12.04.2017 18:18

                      import Module1 from './modules/Module1'
                      import Module2 from './modules/Module2'
                      
                      const combineModuleReducers = modules => {
                        const reducers = {}
                      
                        for (let i in modules) {
                          const red = modules[i].reducer
                          if (typeof(red) !== 'function') {
                            throw new Error('Module ' + i + ' does not define reducer!')
                          }
                      
                          reducers[i] = red
                        }
                      
                        return reducers
                      }
                      
                      const modules = {
                        Module1,
                        Module2
                      }
                      
                      const store = createAppStore(combineReducers(combineModuleReducers(modules)))
                      
                      // код типового модуля
                      const mdl = {
                         title: 'Мой модуль',
                         reducer: (state = initialState) => {/* логика редюсера*/},
                         path: '/module1'
                      }
                      


                      1. TheShock
                        12.04.2017 18:44

                        И что?


                        1. marshinov
                          12.04.2017 19:05

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


                          1. TheShock
                            12.04.2017 19:32

                            Лучше покажите сотни редюсеров, а не код маленькой библиотеки)


                            1. marshinov
                              13.04.2017 16:05

                              export const fetchReducerFactory = (name, initialState, callback) => (state = initialState, action) => {
                                const keys = Object.keys(initialState)
                              
                                for(var i = 0; i < keys.length; i++){
                                  const actionType = name + '/' + toUpperCamelCase(keys[i])+ '/Fetch'
                              
                                  if(action.type == actionType) {
                                    const res = {...state}
                                    res[keys[i]] = Object.assign({}, res[keys[i]], {...action, isFetching: true})
                                    if(typeof(res['ui']) == 'object' && keys[i] == 'data') {
                                      res['ui'] = Object.assign(res['ui'], action.params)
                                    }
                              
                                    return res
                                  }
                              
                                  const actionTypeSucceeded =
                                    (name + '/' + toUpperCamelCase(keys[i])+ '/FetchSucceeded').replace('Data/', '')
                              
                                  if(action.type == actionTypeSucceeded) {
                                    const res = {...state}
                                    res[keys[i]] = Object.assign({}, res[keys[i]], {...action, isInitialized: true, isFetching: false})
                                    return res
                                  }
                              
                                }
                              
                                return typeof(callback) == 'function' ? callback(state, action) : state;
                              }
                              


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

                              Используется так:
                              mdl.reducer = fetchReducerFactory(
                                moduleId, {
                                  ...initialState,
                                  moduleId
                               })
                              


                    1. raveclassic
                      12.04.2017 18:19

                      Ну в опенсорсе этого всего нет по понятным причинам. Но основная мысль, что доменный слой не лежит на уровне редьюсеров. Редьюсеры — это состояние, необходимое только для отрисовки UI и работы самого приложения (всякие там роутинги, состояние сессии и т.п.).
                      Модели же и процессы работы с ними (у нас на сагах все) лежат совершенно отдельно. А там хоть ООП, хоть ФП, хоть что угодно, главное уметь реагировать на экшены запросов (command) и выбрасывать их обратно на обработку редьюсерами (event). Чтение же из стора идет через селекторы (query). Получается этакий CQRS.

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


                      1. TheShock
                        12.04.2017 18:54

                        Ну в опенсорсе этого всего нет по понятным причинам

                        Эмс. Ну на других технологиях довольно много чего лежит в опенсорсе почему-то. Так что причин реальных я не вижу.

                        Я понимаю, как работает Редакс, к сожалению я успел поработать с ним.

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

                        То есть я теперь могу написать любую ересь, а вы не можете привести аргументы, что я пишу ересь?)

                        Я хочу показать, что в реальном коде Редакс приводит к крайне запутаному коду и куче копи-пасты. К сожалению, если я покажу пример — вы скажете, что это не так. Потому я и хочу увидеть реальный код, на котором я мог бы это доказать.

                        К сожалению, все примеры редакса — это что-то из разряда «я написал за пару десятков часов что-то в свое удовольствие и все, что не вписывалось в архитектуру — выбросил как ненужное».

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


                        1. raveclassic
                          12.04.2017 21:16

                          Эмс. Ну на других технологиях довольно много чего лежит в опенсорсе почему-то. Так что причин реальных я не вижу.
                          Под понятными причинами я имел в виду, что у нас все closed-source. Видимо, неудачно выразился.

                          Ну… можно просто порассуждать о возможных проблемах. Если редьюсеры превращаются в кашу с кучей копипасты — с ними точно что-то не так.
                          Мы используем редьюсеры исключительно как хранилище обычных данных (грубо говоря, кэш), состояния сессии, и некоторого состояния UI (но далеко не всего). Гипотетические сотни моделей, если они являются сущностями, могут уложиться в те же сотни редьюсеров с нормализованными данными. Каких-либо проблем я тут не вижу. Если они есть — укажите.

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


                          1. TheShock
                            12.04.2017 22:11

                            Под понятными причинами я имел в виду, что у нас все closed-source. Видимо, неудачно выразился.

                            Ах, я не понял, что вы именно про ВАШ код. Я же говорил впринципе про любой код.

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

                            А то с редаксом всегда так. Из-за Стокгольмского синдрома те, кто его используют — защищают, а показать недостатки на практике — невозможно, потому-что «код закрыт, извините, но честно-честно, там все очень клево, он нас не очень сильно насилует».


                            1. raveclassic
                              12.04.2017 22:25

                              Я же говорил впринципе про любой код.
                              Вот видите, вы говорите, что мои рассуждения неправильные, потому что в реальном коде не так, как я и ожидал. Потому и хотел глянуть на реальный код и ткнуть пальцем, и мне не могли сказать: «но в реальном коде это не так».
                              Ну тут, увы и ах, более менее серьезных примеров в открытом доступе действительно нет. С сагами так вообще все плохо, кроме их документации (кстати, достаточно неплохой) и статей на медиуме из серии «саги для чайников» вообще ничего нет. Приходится искать литературу по process managers и вот этому всему и как-то перекладывать на js.

                              А то с редаксом всегда так. Из-за Стокгольмского синдрома те, кто его используют — защищают, а показать недостатки на практике — невозможно, потому-что «код закрыт, извините, но честно-честно, там все очень клево, он нас не очень сильно насилует».
                              Так а вы покажите с какими недостатками столкнулись вы. А я попробую смоделировать их решение на нашем стэке.


        1. raveclassic
          12.04.2017 12:55
          +2

          Глядя на функцию нельзя однозначно сказать на работу с какой частью стора она заточена.
          Ну так в этом то и смысл. Функция отвязана во всех смыслах от места компоновки. А типизация еще больше упрощает работу.


          1. VolCh
            12.04.2017 14:08

            Ну вот это и не нравится. Мало того, что данные и функция, их мутирующая в разных местах, так она ещё и во всех смыслах полностью отвязана.


            А про типизацию не понял: в сторе редакса же все объекты нетипизированные, по сути исключительно литералы, нельзя же сделать где-то в редьюсере return new User(username), а надо return {username}. Или можно?


            1. raveclassic
              12.04.2017 14:12

              Под типизацией я имел в виду доп. инструменты в виде ts/flow. Вы объявляете тип объекта, с которым работает ваш редьюсер и тот же тип возвращает, и все. Где, в каком месте стейта, это происходит должно быть абсолютно все-равно.

              Как у вас выстроено дерево редьюсеров, что он у вас работает с данными в разных местах? Не очень понимаю.


              1. mkusher
                12.04.2017 14:26

                из приятного в redux4 combineReducers в ts будут использовать mapped types и не надо будет прокидывать руками типы в createStore, а тайпинги эти можно юзать уже сейчас


                1. raveclassic
                  12.04.2017 14:29

                  Ага. Я было PR даже открыл им, а оказывается это уже давно в мастере.


                  1. raveclassic
                    12.04.2017 14:32

                    Точнее в next'е :)


                    1. mkusher
                      12.04.2017 14:55

                      потому что я первый отправил :P


                      1. raveclassic
                        12.04.2017 15:05

                        Вот так всегда… *sigh*


            1. marshinov
              12.04.2017 14:27

              Можно, главное ссылки менять. Просто {...state} не удобно будет делать. А без этого не поменяется ссылка и Redux не запустит перерендер компонентов. Вам просто не нравится, что Redux функциональный.


              1. VolCh
                12.04.2017 14:55
                -1

                Мне не нравится прежде всего, что связка Redux+React предполагает, что нужное состояние DOM является чистой функцией от одного параметра — глобального стора. В теории можно создавать хоть в каждом компоненте сторы как замену this.state и редьюсеры как замену this.setState(), но на практике это не просто.


                1. VasilioRuzanni
                  12.04.2017 15:07
                  +1

                  DOM является функцией от `props` компонента — в ней может быть намешано все, что угодно, хотя обычно бОльшая часть — да, из глобального стора. Вы так говорите, будто это что-то плохое :)

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


                  1. VolCh
                    12.04.2017 15:14

                    Никогда не доверял глобальным переменным :), а по сути стор таковой и является.


                    1. raveclassic
                      12.04.2017 15:20
                      +1

                      Если вы не хотите, чтобы ваши компоненты знали о структуре глобального стора, спрячьте чтение в селекторы, и у вас получится ну 100% CQRS.


                      1. VolCh
                        12.04.2017 15:47

                        Отчасти я наоборот хочу, чтобы знали :)


                        А вообще, глобальный стор редакса со всеми экшенами, которые могут его изменять, у меня чётко ассоциируются с ООП-антипатерном GodObject, а то и с процедурным программированием с большим применением глобальных переменных.


                        1. raveclassic
                          12.04.2017 16:09
                          +1

                          Отчасти я наоборот хочу, чтобы знали :)
                          Вы про контейнеры, надеюсь? Селекторы добавляют дополнительный слой чтения, и позволяют избежать излишнего связывания.

                          А вообще, глобальный стор редакса со всеми экшенами, которые могут его изменять, у меня чётко ассоциируются с ООП-антипатерном GodObject, а то и с процедурным программированием с большим применением глобальных переменных.
                          Вот и зря, так как стор — результат функциональной композиции, а не какая-то глобальная ерунда, к которой все имеют доступ и что-то в ней меняют, как в случае с god-object'ом. То, что стор — javascript объект — это детали реализации.


                          1. VolCh
                            12.04.2017 16:42

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


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


                            1. marshinov
                              12.04.2017 16:46

                              Вот так можно.


                            1. raveclassic
                              12.04.2017 16:49
                              +1

                              Вы можете привести пример? Я никак не пойму, какую задачу вам это «знание пути» помогает решить. Все что вы делаете — лишь прибиваете гвоздями чистую функцию редьюсера к конкретному месту в вашем стейте, когда это прекрасно избегается по-умолчанию с сохранением самого полезного во всем этом безобразии — гибкости композиции.

                              ограничить редьюсеры, которые могут менять участок стора?
                              Я выше вам написал уже, редьюсеры не меняют участки стора, они этот стор создают при композиции.


                              1. VolCh
                                12.04.2017 17:05

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


                                Формально, да, не менют, а создают новый на основе старого. По факту для большинства приложений — это лишь деталь реализации, поскольку новое значение стора просто заменяет старое.


                                1. raveclassic
                                  12.04.2017 17:17

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

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


                                  1. VolCh
                                    12.04.2017 17:49

                                    Разве у редакса есть ограничение, что изменять один участок стейта может один и только один редьюсер? Если есть, то мне надо многое переосмыслить :(


                                    1. raveclassic
                                      12.04.2017 17:58

                                      Ну это же core-принцип. :) Редьюсеры компонуют стейт, а не изменяют его. Невозможно одной сужностью скомпоновать несколько участков — нельзя быть в двух местах сразу. Как только это уляжется, все станет гораздо проще. :)


        1. VasilioRuzanni
          12.04.2017 14:54
          +1

          «Как минимум, MobX нормально оперирует привычными многим бэкендерам полноценными объектами, инкапсулирующими данные и поведение.»

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

          Redux же очень прост, но, по сути, действительно, умеет только хранить состояние, да и все. Ему нужно что-то, что будет поддерживать весь его (потенциально) навороченный стейт в порядке и согласованности. Причем, с ростом проекта, таких сценариев становится все больше, а они сами становятся все сложнее. Нужна асинхронность, опять же. Отсюда и появляются redux-saga, redux-observable (реактивные «саги»), сам я предпочитаю вообще Cycle.js использовать для подобного рода логики.

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


  1. frog
    11.04.2017 21:42

    Мне кажется, в джентльменском наборе из статьи не хватает graphql (+ к нему subscriptions через сокеты)


  1. vanburg
    12.04.2017 04:47

    А я люблю, как вы выразились «хипстерские штучки», и именно такие как я и популяризируют elm и clojure script. Найти работу под эти мощнейшие «штучки» вполне можно. А vanilla js/ts/cs/хзс, спасибо, нет. Хочется получать удовольствие, и спать спокойно.


    1. TheShock
      12.04.2017 05:26

      Вчера Coffee, сегодня Elm, завтра еще что-то. Мода ради моды, которая живет только на волне хайпа.


      1. vanburg
        12.04.2017 06:15
        +1

        Сдается мне вы не вникали в элм, иначе бы не сравнивали сахар с философией.


        1. vintage
          12.04.2017 07:57

          Наконец-то я нашёл эксперта по Elm! Не подскажете, как повесить на элемент пассивный обработчик события? То есть нужен аналог вот этого:


          el.addEventListener( 'wheel' , onScroll , { passive : true } )


  1. basnopisets
    12.04.2017 11:03

    минус только за КДПВ


  1. caballero
    12.04.2017 12:44

    MVC-фреймворк, ORM, Data Mapper, IOC-контейнер, логер, профайлер, очереди, управление конфигурациями, сборка и выкладка

    а кто сказал что все это обязательно надо?
    Не надо явскриптовое безумие переносить обратно на бэкенд.


    1. raveclassic
      12.04.2017 12:57

      Ну а кто сказал, что на клиенте вам обязательно надо все перечисленное в статье?


      1. caballero
        12.04.2017 13:24

        автор

        Итак, нам ПОТРЕБУЮТСЯ:...


        1. marshinov
          12.04.2017 14:24

          Мне действительно все это требуется на фронте. Стек на беке я тоже написал среднестатистический. Очереди не везде конечно, а остальное у нас везде есть в том ли ином виде.