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

Доброго времени суток, 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. Задаем имя и путь для проекта.
1.png

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

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

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

3.png

В код добавляется стандартный код ASP.Net контролера
4.png

return View, Говорит о том что этот контролер должен возвращать представление (index.html). Которое к слову выглядит так:
5.png


Шаг третий.
Далее необходимо подключить midleware в файле Startup.cs. Начнем с метода ConfigureServices.
6.png

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

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

Шаг четвёртый.
Переходим к созданию клиентского приложения. Создаём папку ClientApp в корне проекта
8.png

И открываем её с помощью Visual Studio Code ( так как фронтэнд в нём делать ну просто очень удобно ).
Запускаем Visual Studio Code, кликаем по Open Folder
9.png

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

Шаг пятый.
Инитим проект. В терминале пишем npm init, можно пропустить все вопросы, в конце написать yes и нажать enter.
10.png

В результате у нас создастся файл package.json.
11.png

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

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

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

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

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

14.png


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

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

Далее следует указать, куда webpack должен положить результаты своих стараний. Делается это в свойстве output:
17.png

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

Далее, мы должны сказать webpack’у с какими файлами ему нужно работать. Делается это в свойстве resolve:
19.png

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

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

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

21.png


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

22.png


но перед этим в начале конфигурации, необходимо их подключить
23.png

далее можно указать aliasы для некоторых namespaceов. Мы поступим так с react и react-dom.
24.png

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

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

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


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

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

файл собранный webpack подключится сам. благодаря плагину html.
Создадим файл index.tsx где опишем как и где должно рендерится приложение.
28.png

и напишем скрипт для npm
29.png

обратите внимание что мы копируем react.development.js и react-dom.development.js в папку dist.
Соответственно получаем результат: при запуске в visual studio.
30.png

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

Начинаем:
Создание компонентов подключенных к хранилищу, начинается с создания констант.
31.png

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

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

33.png

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

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

34.png


Так же необходимо создать корневой редьюсер который будет представлять дерево состояний всего приложения:
36.png

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


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

38.png

А затем в том же файле подключить его к Redux
39.png


Далее изменим чуток index.tsx
40.png


Вот и всё. Запустим и проверим что получилось, выполним команду npm run build
41.png

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

Сергей Попов

Кодебай
30.12.2015
4 727
6 723
BIT
444
@mrOkey благодарю за интересные статьи. Было бы здорово залить картинки на форум. Мы не один десяток раз сталкивались с тем, что наши темы остаются без картинок.
 

mrOkey

Well-known member
14.11.2017
967
975
BIT
0
@mrOkey благодарю за интересные статьи. Было бы здорово залить картинки на форум. Мы не один десяток раз сталкивались с тем, что наши темы остаются без картинок.
без проблем. Оставте мне право editа, на час и я залью. Сейчас просто в пути.
 
  • Нравится
Реакции: Сергей Попов
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!