
Настал, наконец, тот момент, когда я могу представить вам боилерплейт React Core Boilerplate, или, иными словами, готовый шаблон проекта на ASP.NET Core.
Интересно? Добро пожаловать под кат.
Основные элементы шаблона:
- NET Core 2 — собственно, основа.
- TypeScript — на нём написан фронтенд.
- React + Server-side rendering (SSR), далее — библиотека, работающая, как на стороне клиента, так и сервера с помощью NodeServices.
- React Helmet — плагин, позволяющий управлять title и meta тегами, работает также вместе с SSR.
- Redux — контейнер состояний приложения.
- SASS — CSS препроцессор стилей.
- Webpack 4 — сборщик модулей.
- Axios — обеспечивает изоморфные fetch-запросы.
- ts-nameof — позволяет с помощью выражений на TypeScript получать путь к вложенным объектам, аля Html.NameFor в Razor; работает вкупе с сериализотором форм NSerializeJson.
Свои разработки, написанные на TypeScript и исправленные мною форки:
- domain-wait — схож с domain-task, но позволяет использовать async/await. Предназначен для ожидания серверным рендерингом fetch-запросов.
- NVal — валидатор форм (приближенный по API с jquery.validation).
- NVal-Tippy — дополнение к NVal, позволяющий с помощью js делать всплывающие ошибки валидации в тултипах.
- NSerializeJson — сериализатор форм в JSON с настройками типов (схожий с serializeJSON).
- bootstrap3-native — исправленный форк native, позволяющий работать с элементами bootstrap 3.
Цель проекта
Цель данного проекта — поддержать разработчиков данного стека: дать возможность сразу приступить к разработке проектов, не заморачиваясь с решением проблем совместимостей технологий в стеке, их настройкой. Поскольку проект создан "для людей" и содержит, на мой взгляд, интуитивно понятную архитектуру, он также будет полезен и новичкам в обучении React, т.к. не содержит проблем "из коробки" для построения приложений.
Детали
С чего всё начиналось
Я — фуллстек разработчик. Когда-то любил писать фронтенд на jQuery — от разработки плагинов до сложных сайтов. Однако в один прекрасный день стало ясно: поддержка сайтов на этой библиотеке тем труднее, чем больше проект. Тогда мною было решено заняться изучением других инструментов для разработки фронтенда. Angular мне не понравился из-за своей толстой абстракции и, предварительно изучив достоинства и недостатки каждого фреймворка и библиотеки, я сделал выбор в пользу React, в котором привлекло то, что HTML и TypeScript можно писать код в одном месте (.tsx-файле), не переключаясь между HTML и файлами скриптов.
Со временем я полюбил React. Но, признаться честно, поначалу приходилось применять jQuery и в React из-за плагинов, которые его требуют. А затем, когда я пытался освоить серверный рендеринг из-за известных проблем SPA и SEO, я узнал, что jQuery либо нельзя с ним подружить, либо это будет костыльно (через jsdom, т.к. требуется объект window). Я знал, что этих jQuery-плагинов мне будет не хватать. И с тех пор я начал разрабатывать плагины на "Vanilla JS", которые чем-то схожи по API с плагинами, работающими на jQuery.
Серверный рендеринг
Несколько месяцев назад Microsoft выпустила обновление для Visual Studio 2017, в котором был шаблон проекта ASP.NET Core 2 + TypeScript + React + Redux + Webpack с серверным пререндером с помощью NodeServices. Изучая шаблон, я потратил немало времени, чтобы подключить туда SASS через WebPack и обновить последний, поскольку, изучая документацию, форумы и StackOverflow, я не находил нужных мне ответов по настройке этого стека, в то время, как документация была, в основном, по стеку с Angular. Спустя некоторое время Microsoft заменила этот шаблон на create-react-app без TypeScript и серверного рендеринга. Судя по комментариям в интернетах, многих других, как и меня, это смутило. Однако у меня сохранился тот шаблон, и я решил из него сделать свой с блекджеком и плюшками, разобравшись с ним и решив все проблемы разом.
P.S.: Недавно читал, что существуют, так называемые, противники изоморфного подхода к разработке приложений. А как же SEO? Или вы предлагаете рендерить всё платными сторонними сервисами? Насколько я знаю, гугл не всегда может индексировать толстого клиента без пререндера. Ну да ладно.
Техническая часть
React, React Router, Redux
- Обе библиотеки обновлены до последней версии, кроме react-router-redux, с ней что-то не так.
- Добавлены примеры их совместного использования, в том числе использование Route вкупе с разными Layout (компонент AppRoute).
WebPack и модули
- Ввиду своей особой структуры конфигурации WebPack, а, именно, его разделения на серверную и клиентскую часть, для подключения загрузчиков и обновления WebPack пришлось применить смекалку: поставить заглушку для SASS в серверной конфигурации WebPack, чтобы собирать стили только в клиентской части (style-loader требует наличие window). А если использовать Extract CSS плагин (ныне MiniCssExtractPlugin для WebPack 4), тогда, при обновлении файлов, не работал Hot Module Reload (HMR). Поскольку изначально в шаблоне от Microsoft были конфигурации для старых версий библиотек и самого WebPack, многое пришлось перепиливать.
- Были добавлены алиасы часто используемых путей в WebPack и tsconfig.json.
- awesome-typescript-loader заменён на babel-loader.
NodeServices
Документации по NodeServices было мало для решения каких-то проблем: то HMR отваливался, то что-то не работало. Однако, проявив терпение, спустя полтора месяца, наконец-таки, у меня получилось разрешить критические моменты:
⬝ Вместо атрибута "asp-prerender-module" для DIV-элемента во View Razor’a, пришлось инжектить в него ISpaPrerender:
@inject Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerenderer prerenderer
и, выполнять вручную скрипт:
var prerenderResult = await prerenderer.RenderToString(%путь к скрипту загрузки серверного рендера%, customDataParameter: data);
Затем, из объекта prerenderResult можно вытаскивать отрендеренный HTML React’a и React Helmet в блоки BODY/DIV и HEAD, соответственно.
⬝ NPM-пакет domain-task не позволял использовать async/await и инструментов для fetch-запросов кроме isomorfic-fetch и fetch из этого же npm-пакета. Это исправлено мною в пакете domain-wait. Теперь fetch-запросы писать приятнее, особенно, используя axios.
Архитектура приложения
Из личного опыта вынес некоторые наработки, например, во все методы сервисов (кроме авторизации) принимать ServiceUser и возвращать объект Result с результатом или ошибками. Оба таких объекта + имитация сервиса авторизации уже заложена в боилерплейт. Я противник Identity, да и авторизация не всем подойдёт. Так что, ничего лишнего, да и имитация авторизации легко выпиливается (AccountService, Middleware, ControllerBase).
- Два React’овских Layout — гостевой и для авторизованных пользователей.
- Подключены и написанные мною плагины, которые доступны в NPM и активно используются. Теперь легко интегрировать формы с валидацией, и вытаскивать из них JSON данные. Всё это демонстрируется в примере, находящемся в шаблоне.
- На стороне frontend’a все fetch-запросы инкапсулированы в сервисы, которые ещё и изоморфные, т.е. могут работать и на стороне сервера.
- В проекте приведён пример того, как, на мой взгляд, должно выглядеть приложение: легко читаемые и интуитивно понятные структура приложения и код.
| .gitignore | AppSettings.cs | appsettings.Development.json | appsettings.json | Constants.cs # Константы, содержат ключи от куки для фейковой авторизации. | package.json # Файл NPM. | Program.cs # Входная точка приложения. | ReactSSR.WebApp.csproj # Файл проекта Visual Studio 2017. | README.md | Startup.cs # Содержит настройки приложения и middleware фейковой авторизации. | tsconfig.json # Файл конфигурации TypeScript. | webpack.config.js # Содержит конфигурации WebPack для сборки серверного и клиенсткого бандлов. | webpack.config.vendor.js # Содержит конфигурации WebPack для серверного и клиентского Vendor-бандлов. | +---ClientApp | | boot-client.tsx # Входная точка для рендеринга фронтенда в браузере. | | boot-server.tsx # Входная точка для рендеринга фронтенда на стороне сервера. | | configureStore.ts # Конфигурация хранилищ Redux. | | global.d.ts # Глобальные определения модулей и типов TypeScript для фронтенда (например, ts-nameof, \*.png, и т.д.) | | Globals.ts # Инкапсулирует изоморфное состояние приложения (например, данные об авторизации). | | routes.tsx # Настройки маршрутизации для фронтенда. | | Ui.ts # Включает хелперы для UI (например, всплывающие подсказки). | | utils.ts # Содержит полезные методы. | | | +---components # Компоненты (не страницы). | | +---person | | | PersonEditor.tsx # Компонент, входящий в состав примера. | | | | | \---shared # Общие компоненты. | | AppRoute.tsx # Компонент для построения маршрутов с более, чем одним лэйаутом. | | ErrorBoundary.tsx # Компонент, основанный на паттерне "error boundary". При обёртке в него, помогает отлавливать ошибки в других компонентах. | | Footer.tsx # Футер для авторизованной зоны. | | Loader.tsx # Компонент, содержащий индикатор загрузки. | | PagingBar.tsx # Переключатель страниц. | | TopMenu.tsx # Верхнее меню для авторизованной зоны. | | | +---images | | logo.png # Логотип бойлерплейта. | | | +---layouts # Слои (зоны). | | AuthorizedLayout.tsx | | GuestLayout.tsx | | | +---models # Модели TypeScript, используемые в приложении. | | ILoginModel.ts | | IPersonModel.ts | | IServiceUser.ts | | ISessionData.ts | | Result.ts # Реализация паттерна "Result". | | | +---pages # Страницы приложения. | | ExamplePage.tsx | | HomePage.tsx | | LoginPage.tsx | | | +---services # Изоморфные JS-сервисы, инкапсулирующие логику для работы с запросами. | | AccountService.ts # JS-сервис фейковой авторизации. | | PersonService.ts # JS-сервис - пример. | | ServiceBase.ts # Базовый абстрактный TS-класс для построения изоморфных JS-сервисов. | | | +---store # Хранилища Redux. | | index.ts # Определения для хранилищ Redux. | | LoginStore.ts | | PersonStore.ts # Хранилище для примера. | | | \---styles | authorizedLayout.scss # Стили для авторизованной зоны. | guestLayout.scss # Стили для гостевой зоны. | loader.scss # Стили для индикатора загрузки. | main.scss # Общие стили. | preloader.css # Стили для первоначального индикатора загрузки. | +---Controllers | AccountController.cs # Контроллер фейковой авторизации. | ControllerBase.cs # Инкапсулирует свойства и настройки для фейковой авторизации. | MainController.cs # Контроллер входной точки. | PersonController.cs # Контроллер-пример.. | +---Extensions | ServiceCollectionExtensions.cs # Инкапсулирует методы, позволяющие определять Lazy DI контейнеры. | +---Infrastructure # Папка, содержащая основные модели инфраструктуры приложения. | Result.cs # Реализация паттерна "Result" на стороне сервера. | ServiceBase.cs # Базовый класс для всех сервисов, реализующих паттерн Facade/Фасад. | +---Models | LoginModel.cs # Модель для фейковой авторизации. | PersonModel.cs # Модель для примера. | ServiceUser.cs | SessionData.cs # Модель данных изоморфной сессии. | +---Services # Содержит сервисы, реализующие паттерн "Facade". | AccountService.cs # Сервис фейковых аккаунтов. | PersonService.cs # Сервис-пример. | +---Views | | \_ViewImports.cshtml | | \_ViewStart.cshtml | | | +---Main | | Index.cshtml # Входная точка приложения, содержащая корневой контейнер, в который рендерится фронтенд. | | | \---Shared | Error.cshtml | \---wwwroot # Корневая папка, в которую будут собираться бандлы. favicon.ico
На этом всё. Всем спасибо за внимание и, конечно же, Happy Coding!
ссылка на оригинал статьи https://habr.com/post/426147/
Добавить комментарий