Статья Упрощаем разработку typescript, react, redux, axios, less

mrOkey

mrOkey

Red Team
14.11.2017
621
717
Доброго времени суток, codeby. Сегодня расскажу как подружить легендарный стэк react+redux с typescript. Есть два способа - начнем со сложного, простой способ подразумевает использование create-react-app.
Зачем вообще это делать? Основная причина в том же, в чём и основное преимущество typescriptа над javascript. Это типизация. И да это действительно преимущество. У TypeScripta она очень продуманная, а как следствие позволяет делать просто, сложные вещи. Очень грубо говоря, typescript - это расширения языка javascript, основное преимущество которого это строгая типизация. И благодаря ей мы можем быстрее разрабатывать, достигается это за счёт подсветок от IDE, проверки типов при компиляции и так далее.

Если вы разрабатывали что-то больше 100 000 строк кода на javascript, вы хоть раз да сталкивались с недостатком строгой типизации. Не случайно ведь Facebook переписывает свой проект jest ( фреймворк для тестирования frontend ) с flow ( проекта Facebook для создания строгой типизации ) на typescript. Что я нахожу весьма забавным. Кроме того на type script переписан гигант Angular.
Вообщем, думаю вы поняли что это одно из самый крутых изобретений, придуманных, внезапно, microsoft.


Начнем создавать наш симбиоз крутых вещей для typescript.
Первым делом создадим asp.net core web api проект. Должен предупредить, что visual studio умеет создавать шаблонный проект на javascript reacte. Моё мнение таково - это шлак, там очень много мусора, который нужно будет выпиливать, однако тут встает другой вопрос: что проще - выпилить мусор или создать с нуля. Лично мне, кажется, что создание с нуля выглядит проще.

Шаг первый.
Открываем Visual Stuido, далее File > New project. В открывшемся диалоге Visual C# > .NET Core > ASP.NET Core Web Application. Задаем имя и путь для проекта.
Упрощаем разработку typescript, react, redux, axios, less

Шаг второй
Выбираем Web application проект. Удаляем лишний мусор из проекта, чтобы получилось как то так:
Упрощаем разработку typescript, react, redux, axios, less

Далее нам нужно создать один единственный контролер для одной единственной страницы ( у нас ведь single page application, не так ли ).

Делается это так:
В корне проекта создаётся папка Controllers

Упрощаем разработку typescript, react, redux, axios, less

В код добавляется стандартный код ASP.Net контролера
Упрощаем разработку typescript, react, redux, axios, less

return View, Говорит о том что этот контролер должен возвращать представление (index.html). Которое к слову выглядит так:
Упрощаем разработку typescript, react, redux, axios, less


Шаг третий.
Далее необходимо подключить midleware в файле Startup.cs. Начнем с метода ConfigureServices.
Упрощаем разработку typescript, react, redux, axios, less

на 28 строке, мы сообщаем, что сообираемся использовать файлы для Single Page Application, причем будем использовать нестандартную папку для этого - ClientApp.
Переходим к методу Configure
Упрощаем разработку typescript, react, redux, axios, less

Тут мы так же сообщаем что собираемся использовать SPA файлы, а на 48-54 строке мы настраиваем суть одностраничного приложения - все запросы будут отправлены на один единственный контролер

Шаг четвёртый.
Переходим к созданию клиентского приложения. Создаём папку ClientApp в корне проекта
Упрощаем разработку typescript, react, redux, axios, less

И открываем её с помощью Visual Studio Code ( так как фронтэнд в нём делать ну просто очень удобно ).
Запускаем Visual Studio Code, кликаем по Open Folder
Упрощаем разработку typescript, react, redux, axios, less

Указываем путь к папке ClientApp. Щелкаем выбор папки. Дальнейшая работа происходит в консоли Visual Studio Code. Кликаем Ctrl + ~ (Тильда) и нас приветствует терминал.

Шаг пятый.
Инитим проект. В терминале пишем npm init, можно пропустить все вопросы, в конце написать yes и нажать enter.
Упрощаем разработку typescript, react, redux, axios, less

В результате у нас создастся файл package.json.
Упрощаем разработку typescript, react, redux, axios, less

Далее необходимо установить необходимые пакеты.

Шаг шестой.
Первым делом поставим webpack. Для этого используем команду npm i webpack --save-dev. Webpack - это очень мощный и умный сборщик для фронтенда, да да как Gulp или Grunt, только в разы умнее. Далее необходимо поставить следующие пакеты.
Раскрытие возможностей webpack выходит за рамки этой статьи. При многочисленных просьбах в комментариях, могу написать и на эту тему.
Упрощаем разработку typescript, react, redux, axios, less

Любителям copy-paste в конце будет ссылка на репозиторий, который можно использовать как шаблон проекта. Также нам нужны следующие пакеты
Упрощаем разработку typescript, react, redux, axios, less

наибольший интерес для нас представляют пакеты с префиксом @types. Это типизация typescripta для пакетов javascript пакетов. Нужны они, чтобы мы могли работать с типизированными данными. Awesome-typescript-loader нужен для webpack, чтоб он понимал как работать с ts.

Шаг седьмой.
Компилятор TypeScript требует файл tsconfig.json. давайте его создадим, так же в корне проекта. О настройках можно почитать на офф сайте, поэтому я не вижу смысла дублировать сюда перевод

Упрощаем разработку typescript, react, redux, axios, less


Шаг восьмой.
Переидём к конфигурации webpack. В корне ClientApp создадим файл webpack.config.js кстати, Visual Studio Code умеет его определять так что выглядеть это будет так:
Упрощаем разработку typescript, react, redux, axios, less

Вся экосистема webpack построена на модулях ( лоадерах ). Для каждой фишки используется свой лоадер, для картинок url-loader и file-loader, для less - less-loader и так далее. Мы устанавливали лоадеры во время установки пакетов.
Любая wepack-конфигурация начинается с определения входного файла. Это такой файл - откуда, следую дереву зависимостей, вебпак будет добавлять js-файлы в финальный bundle. У нас такой файл будет называться index.tsx ( расширение tsx аналог jsx только для typescript ). Итак первое что мы делаем указываем входной файл
Упрощаем разработку typescript, react, redux, axios, less

Далее следует указать, куда webpack должен положить результаты своих стараний. Делается это в свойстве output:
Упрощаем разработку typescript, react, redux, axios, less

переменная __dirname существует в системе node.js ( собственно node и запускает webpack ) значение этой переменной присваивается компилятором node, и соответствует пути до папки в которой находится файл с вызовом переменной.
Далее имеется возможность указать инструменты разработки: sourcemap, devserver и так далее. Не будем ей пренебрегать и попросим webpack создавать sourcemap ( штука нужна для отладки в консоли разработчика браузера, без неё код в консоли выглядел бы минифицированным, что очень плохо читается ).
Упрощаем разработку typescript, react, redux, axios, less

Далее, мы должны сказать webpack’у с какими файлами ему нужно работать. Делается это в свойстве resolve:
Упрощаем разработку typescript, react, redux, axios, less

Таким образом мы подключили файлы c расширением ts, tsx, js, json.

Далее идет самая важная часть конфигурации. Мы должны как webpack должен работать с файлами, какой лоадер он должен использовать для файлов tsx, а какой для png. делается это в свойстве module.rules:
Упрощаем разработку typescript, react, redux, axios, less

Таким образом, мы говорим, что для файлов tsx ( файлы чаще всего с реактовскими компонентами ) использовать загрузчик awesome-typescript-loader. Далее указываем для всего остального:

Упрощаем разработку typescript, react, redux, axios, less


Затем имеется возможность указать набор плагинов для webpack, их мы тоже установили. В моём случае я использую два плагина CleanWebpackPlugin и HtmlWebpackPlugin. Название говорит само за себя. Объявляются и настраиваются они так:

Упрощаем разработку typescript, react, redux, axios, less


но перед этим в начале конфигурации, необходимо их подключить
Упрощаем разработку typescript, react, redux, axios, less

далее можно указать aliasы для некоторых namespaceов. Мы поступим так с react и react-dom.
Упрощаем разработку typescript, react, redux, axios, less

На этом конфигурация закончена. Как видите ничего сложного. Можно поставить точку.

Шаг девятый.
Переходим к структуре проекта: вещь весьма важная. Я стараюсь придерживаться стандартной структуре, хоть её и критикуют. Для начала в корне проекта создаём папки src и dist, для исходных кодов и для результатов работы webpack.

В папке src создаём файл index.tsx и index.html, (ещё я создаю appSettingsProvider.json ) чтобы прокидывать настройки с backend.
Также в этой папке создаём следующие папки:
  • Actions - для хранения ActionCreators. Фишка редукса ( напоминаю, что в данной статье не рассматривается реакт и редукс. При желании, напишите в комментариях, чтоб я знал о чём можно рассказать )
  • Components - для хранения реакт компонентов
  • Constants - для редукс - констант ( названия Action’ов для редукса )
  • Interfaces - для хранения наших типов
  • Layouts - для компонентов высшего порядка
  • Reducers - для хранения редьюсеров соответствено
  • Store - для редукс-хранилища
После всех преобразований структура выглядит следующим образом:
Упрощаем разработку typescript, react, redux, axios, less


Шаг десятый.
Напишем простой hello world на нашей структуре.
Первое, что необходимо знать о реакт приложениях - всё есть компонент. Компонент - это минимальная, неделимая часть интерфейса. Каждый пользовательский интерфейс состоит из частей. Компоненты позволяют многократно применять одну и ту же структуру Dom для различных наборов данных. Обдумывая пользовательский интерфейс, создаваемый с помощью React разбейте ваши элементы на многократно используемые части. Компоненты будем хранить, внезапно, в папке components.
Создадим компонент Hello. Этот компонент будет презентационным, т.е. он будет только отображать данные которые ему дадут. Он не будет ничего знать о редуксах, и прочей части приложения. Простейший компонент выглядит так:
Упрощаем разработку typescript, react, redux, axios, less

Создадим в файле index.html div с ID = app. В этот див мы будем рендерить реакт приложение. так же подключим реакт development ( для упрощения отладки ).
Упрощаем разработку typescript, react, redux, axios, less

файл собранный webpack подключится сам. благодаря плагину html.
Создадим файл index.tsx где опишем как и где должно рендерится приложение.
Упрощаем разработку typescript, react, redux, axios, less

и напишем скрипт для npm
Упрощаем разработку typescript, react, redux, axios, less

обратите внимание что мы копируем react.development.js и react-dom.development.js в папку dist.
Соответственно получаем результат: при запуске в visual studio.
Упрощаем разработку typescript, react, redux, axios, less

Далее подключим для нашего шаблона Redux. В папке Layout создадим компонент высшего порядка HelloWrapper. Такие компоненты при необходимости вызывают actions creators и отправляют компоненты на рендеринг.
приветствовать будем покемонов с pokeapi.co.

Начинаем:
Создание компонентов подключенных к хранилищу, начинается с создания констант.
Упрощаем разработку typescript, react, redux, axios, less

Затем необходимо создать ActionCreator в папке Actions. А так же объявить интерфейс IAction, чтобы воспользоваться преимуществами TypeScript.

Важное правило Redux: состояние приложения должно хранится в одном неизменяемом объекте. Неизменяемость означает, что этот объект состояния не должен изменяться. Действия — это инструкции, касающиеся изменений, вносимых в состояние приложения, которые сопровождаются данными, необходимыми для внесения изменений. Действия являются единственным способом обновить состояния Redux-приложения. Они предоставляют нам инструкции о том, что должно быть изменено, но мы можем рассматривать их и в качестве записей истории изменений, произошедших со временем. При создании Redux-приложения хотелось бы сместить мышление в сторону концентрации на действиях. Как действия повлияют на данные состояния? После выявления действий их можно перечислить в файле constants.js. Действия представляют собой объект, имеющий как минимум поле типа: Тип действия является строкой, определяющей то, что должно произойти. ACTION представляет действие. Обычно типы действий прописываются заглавными буквами со знаками подчеркивания вместо пробелов. Нужно также стремиться чётко формулировать предназначение действия. Целевые данные действия. Действия - литералы предоставляющие небольшие инструкции, необходимые для внесения изменений в состояние. Большинству изменений состояния также нужны данные. На такие данные мы ссылаемся как на целевые данные действия.
Действия, представленные в виде компактных пакетов, сообщающих Redux, как должно изменится состояние. В них также включены все данные, которые понадобится Redux для внесения изменения.
Упрощаем разработку typescript, react, redux, axios, less

Упрощаем разработку typescript, react, redux, axios, less

Далее, создаём редьюсер.
В Redux модульность достигается за счёт функций. Они используются для обновления частей дерева состояния. Эти функции называются преобразователями (reducers). Преобразователи представляют собой функции, которые получают текущее состояние и действие в виде аргументов и используют их для создания и возвращения нового состояния. Разрабатываются для обновления конкретных частей дерева состояния : либо листьев, либо ветвей. Затем преобразователи можно собирать в один управляющий всем состоянием приложения при любых действиях. Для работы с каждой частью дерева состояния используется отдельный преобразователь. Каждый преобразователь предназначен только для тех действий, которые необходимы ему для обновления его частей дерева состояния. Используется switch-case по типам действия. Преобразователь всегда должен что-то возвращать, для блока default можно вернуть текущее состояние. В преобразователях не должно быть побочных эффектов Обновлением состояний занимаются преобразователи, которые являются чистыми функциями, получившими в качестве аргумента состояние, а в качестве второго аргумента - действие. Преобразователи не вызывают побочных эффектов и должны рассматривать свои аргументы как неизменяемые объекты. Модульность в redux достигается за счёт преобразователей. В итоге, преобразователи сводятся в одну функцию способную обновить всё дерево состояний.

Выглядит так:

Упрощаем разработку typescript, react, redux, axios, less


Так же необходимо создать корневой редьюсер который будет представлять дерево состояний всего приложения:
Упрощаем разработку typescript, react, redux, axios, less

Следующим шагом, идёт создание хранилища. При использовании Redux управление состоянием полностью удаляется из React. Состоянием управлять будет Redux. При создании Redux приложений состояние является первым, о чём нужно подумать. Постарайтесь определить его в одном объекте. Обычно рекомендуется составлять черновой JSON-проект вашего дерева состояния с местами предназначенными для заполнения данными. В redux хранилищем считается то место, где хранятся данные, состояния приложения и обрабатываются все обновления состояния. Хранилище занимается обновлением состояния, пропуская текущее состояние и действие через единый преобразователь. Мы создадим его путём сочетания и составления в него наших преобразователей. В redux имеется специально предназначенная функция combineReducers, которая сводит все преобразователи в единый. Эти преобразователи используются для создания вашего дерева состояний имена полей соответствуют именам переданных преобразователей. Хранилище также может быть создано с начальными данным. Единственным способом изменения состояния вашего приложения является диспетчеризация действий через хранилище. В нем имеется метод dispatch, готовый получить действия в виде аргумента. При диспетчеризации с помощью хранилища действие проводится через преобразователи и состояние обновляется.
Упрощаем разработку typescript, react, redux, axios, less


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

Упрощаем разработку typescript, react, redux, axios, less

А затем в том же файле подключить его к Redux
Упрощаем разработку typescript, react, redux, axios, less


Далее изменим чуток index.tsx
Упрощаем разработку typescript, react, redux, axios, less


Вот и всё. Запустим и проверим что получилось, выполним команду npm run build
Упрощаем разработку typescript, react, redux, axios, less

а во время загрузки вместо надписи hello bulbasaur написано Loading. Приложение работает как мы того ожидали.
На этом у меня всё. Ссылка на репозиторий:
 
Последнее редактирование:
The Codeby

The Codeby

ООО Кодебай
30.12.2015
3 228
4 630
@mrOkey благодарю за интересные статьи. Было бы здорово залить картинки на форум. Мы не один десяток раз сталкивались с тем, что наши темы остаются без картинок.
 
mrOkey

mrOkey

Red Team
14.11.2017
621
717
@mrOkey благодарю за интересные статьи. Было бы здорово залить картинки на форум. Мы не один десяток раз сталкивались с тем, что наши темы остаются без картинок.
без проблем. Оставте мне право editа, на час и я залью. Сейчас просто в пути.
 
  • Нравится
  • Не нравится
Реакции: h4x0r и The Codeby
Мы в соцсетях:  ТелеграмВконтактеДзенФейсбукТвиттерЮтуб