Создадем проект на React, Vite и TypeScript

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

Итак, давайте создадим наш проект React + Vite + TypeScript!

Традиционно вы могли бы подумать о том, что мы будем использовать именно Create-React-App (CRA). Но что, если я скажу вам, что есть лучшая альтернатива, обеспечивающая:

  • помощь на пути создания компонентов, маршрутизации и т.д.
  • автоматическое предоставление инструментов для тестирования e2e, модульного тестирования, форматирования кода и компоновки
  • встроенную поддержку Vite и Vitest (в качестве альтернативы Webpack и Jest)
  • кэширование ваших скриптов, чтобы ускорить работу
  • помощь в модуляризиции вашего приложения
  • запуск функций автоматического обновления, которые позволят сохранить актуальность вашего приложения надолго

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

Читатель: “Подождите минутку, я слышал о Nx. Разве это не для монорепозитория?”
Я: “Да, ты прав. Но в 15.3 они ввели нечто под названием ”автономные приложения””
Читатель: “Автономный?”
Я: “Да, модный термин для настройки одного приложения, допускающий некоторую классную модульность. Здесь есть видео, представляющее эту функцию: https://youtu.be/qEaVzh-oBBc
Читатель: “Хм, интересно 🤔”

Итак, давайте пойдем и создадим наш проект с использованием React + Vite + TypeScript.

@javascriptv – продвинутый Javascript в телеграм.

Создание нового проекта

Чтобы создать новый проект, просто вызовите следующую команду:

npx create-nx-workspace@latest awesomereactapp --preset=react-standalone

Обратите внимание, что awesomereactapp – это имя создаваемого приложения и папки, а --preset=react-standalone указывает Nx, какой шаблон использовать при развёртывании начальной настройки. Вы также можете вызвать его следующим образом:

npx create-nx-workspace@latest awesomereactapp

А затем выберите предпочитаемый вами вариант в командной строке терминала:

Создадем проект на React, Vite и TypeScript

В конце концов, вы получите следующую структуру:

Создадем проект на React, Vite и TypeScript

Запуск, сборка и тестирование приложения

Во-первых, давайте запустим наше новое приложение. Просто вызовите…

npm start

… и в считанные миллисекунды ваше приложение должно обслуживаться через http://localhost:4200 /.

npm start просто вызывает скрипт, определённый в package.json:

// package.json
{
  "name": "awesomereactapp",
  ...
  "scripts": {
    "start": "nx serve",
    "build": "nx build",
    "test": "nx test"
  }
  ...
}

Он передаётся nx serve, где serve – это Nx target (цель), которая должна быть вызвана. Вы можете найти её в project.json:

// project.json
{
  "name": "awesomereactapp",
  "$schema": "node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "./src",
  "projectType": "application",
  "targets": {
    "build":  {...},
    "serve": {
      "executor": "@nrwl/vite:dev-server",
      "defaultConfiguration": "development",
      "options": {
        "buildTarget": "awesomereactapp:build"
      },
      "configurations": {
        "development": {
          "buildTarget": "awesomereactapp:build:development",
          "hmr": true
        },
        "production": {
          "buildTarget": "awesomereactapp:build:production",
          "hmr": false
        }
      }
    },
    "test": {...},
    "lint": {...}
  },
  "tags": []
}

Здесь вы можете увидеть все цели, доступные для данного проекта, а также можете добавить свои собственные! В двух словах, Nx target содержит:

  • executor – функция (здесь dev-server), являющаяся плагином (здесь @nrwl/vite) для выполнения поставленной задачи. Думайте об этом как об оболочке ваших обычных
    Npm скриптов;
  • options – с помощью данной функции, вы можете передать параметры для executor;
  • configurations – позволяет создавать различные версии options . Вы контролируете, какая конфигурация используется, передавая её через --configuration=production вашим командам. Также обратите внимание на конфигурацию по умолчанию.

Вы можете более подробно ознакомиться с Nx docs, если хотите.

Создание приложения

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

npx nx build

Это помещает выходные данные в папку dist. Теперь, когда мы увидели целевые объекты project.json, вы, вероятно, догадались, что вы можете настроить эту выходную папку в этих настройках.

Тестирование приложения

Вы сразу можете провести тестирование, выполненное с использованием Vitest. Думаю, вы уже догадались, что можете просто запустить тесты с помощью команды…

npx nx test

Запуск интеграционных тестов с Cypress

Возможно, вы обратили внимание на папку e2e. Это полностью функционирующая установка Cypress для выполнения интеграционных или даже полных тестов.

Это отлично, потому что вам вообще не нужно ничего настраивать. Нет необходимости в…

  • других утилитах, ведь Cypress настроен на использование Vite (вместо Webpack)
  • раскручивании нашего сервера разработки вручную, чтобы мы могли загрузить его в нашу среду Cypress tests

Всё, что нам нужно сделать, это использовать команду…

npx nx e2e e2e

Поначалу, это может показаться странным, но в основном мы запускаем таргет e2e (см. e2e/project.json) в проекте e2e.

// e2e/project.json
{
  "name": "e2e",
  "$schema": "../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "e2e/src",
  "projectType": "application",
  "targets": {
    "e2e": {
      "executor": "@nrwl/cypress:cypress",
      "options": {
        "cypressConfig": "e2e/cypress.config.ts",
        "devServerTarget": "awesomereactapp:serve:development",
        "testingType": "e2e"
      },
      "configurations": {
        "production": {
          "devServerTarget": "awesomereactapp:serve:production"
        }
      }
    },
    ...
  }
}

По умолчанию, эти тесты выполняются в headless mode (безголовом режиме), но вы можете воспользоваться --watch, чтобы запустить тесты в интерактивном режиме с Cypress test runner. Тогда они будут выполняться повторно всякий раз, когда мы будем менять наш исходный код.

Линтинг

Линтинг можно запустить, выполнив следующую команду:

npx nx lint

В корне рабочей области уже есть файл .eslintrc.json, содержащий некоторые правила для линтинга.

Настройка Vite и Vitest

Установка проекта выполнена таким образом, что вы можете легко настроить Vite и Vitest. Просто откройте предварительно сгенерированный файл vite.config.ts в корне вашей рабочей области и добавьте пользовательские плагины Vite или настройте Vitest.

/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsConfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  server: {
    port: 4200,
    host: 'localhost',
  },
  plugins: [
    react(),
    viteTsConfigPaths({
      root: './',
    }),
  ],
 // vitest config
  test: {
    globals: true,
    cache: {
      dir: './node_modules/.vitest',
    },
    environment: 'jsdom',
    include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
  },
});

Кэширование

Nx известен своим кэшированием, которое помогает оптимизировать скорость в монорепозитории. Кэширование принимает входные данные (команду, исходные файлы, переменные среды…) и вычисляет хэш.

Создадем проект на React, Vite и TypeScript

При каждом запуске, Nx сравнивает этот хэш с локальной папкой кэша. Если хэш существует, Nx восстанавливает вывод командной строки и потенциальные артефакты (JS, CSS,… файлы), созданные предыдущим запуском. Это помогает ускорить вычисления, потому что вы не запускаете их, если вам это не нужно.

Документы по Nx для получения большего количества информации: https://nx.dev/concepts/how-caching-works

Пока это, очевидно, имеет большой смысл в монорепозитории, это может помочь ускорить работу над проектом. Большинство проектов имеют несколько целей, таких как сборка, тестирование, линтинг. Они также могут быть кэшированы! Представьте, что у вас есть PR, в котором вы изменяете некоторые файлы *.spec.ts, потому что добавляете тест или исправляете некоторые из них. Ваш сценарий CI, вероятно, постоянно запускает все целевые объекты (build, test, lint). И он должен полностью это сделать. Но вы могли бы избежать этапа сборки, потому что ваш файл спецификации не должен влиять на результат. Таким образом, он может быть восстановлен из кэша. Необходимо запустить test и, возможно, lint, если вы запускаете линтинг также для файлов спецификаций.

Вы можете точно настроить то, что попадает в кэш для каждой команды. Подробнее в Nx документации .

Модули вашего приложения

Представьте себе приложение на витрине магазина. Вероятно, у вас будут такие сущности, как:

  • Список продуктов — в котором будут перечислены все доступные в настоящее время продукты, их рейтинги, отзывы пользователей и т.д
  • Заказы — для просмотра ваших текущих открытых заказов от имени пользователя или просмотра прошлых заказов. Такие вещи, как размещение нового заказа или возврат средств за ранее приобретённый товар
  • Платежи — для обработки потока платежей, запроса кредитной карты, запуска платежа и запуска процесса размещения заказа после успешного завершения платежа
  • Аутентификация — которая обрабатывает весь процесс регистрации / входа в систему и предоставляет утилиты более низкого уровня для других доменных областей в приложении, например, для получения доступа к текущему пользователю.
  • Профиль пользователя — который управляет всем, что связано с пользователем. Подумайте об этом, когда вы зайдете свою учётную запись на Amazon.

Единственный способ управлять такой структурой с помощью текущего инструментария (включая CRA) – организовать эти сущности в папках. Таким образом, у вас было бы что-то подобное в настройке CRA:

cra-app
  ├─ public/
  ├─ src/
  │   ├─ authentication/
  │   │  ├─ current-user/
  │   │  │   ├─ ...
  │   │  │   └─ index.ts
  │   │  ├─ login/
  │   │  └─ signup/
  │   ├─ orders/
  │   │   ├─ checkout/
  │   │   ├─ place-order/
  │   │   ├─ refund/
  │   │   └─ order-list/
  │   ├─ payments/
  │   ├─ products/
  │   ├─ user-profile/
  │   │   ├─ addresses/
  │   │   └─ credit-cards/
  │   ├─ App.css
  │   ├─ App.tsx
  │   ...
  ├─ package-lock.json
  ├─ package.json 
  └─ README.md

Большинство devtools (включая CRA) вынуждают вас создавать монолитную структуру, в которой вы разделяете свои функции по папкам. Однако папки ограничены с точки зрения изоляции; по мере роста вашего приложения, всё это может быстро выйти из-под контроля.

Мы можем создать другую, более прочную структуру с помощью Nx, выделив эти области в специальные библиотеки или модули. Они работают бок о бок с вашим приложением. Допустим, у нас есть папка с именем “domains”, которая содержит эти доменные области. Затем вы можете легко сгенерировать новую библиотеку с помощью следующей команды:

npx nx g @nrwl/react:lib checkout --directory=domains/orders/checkout --bundler=none

Приведённая выше команда создаёт новую “cheсkout” библиотеку в папке domains/orders/. Вот как это выглядит:

awesomereactapp
├─ public
│  └─ favicon.ico
├─ src
│  ├─ app
│  ├─ ...
├─ domains
│  └─ orders
│     └─ checkout
│        ├─ src
│        │  ├─ index.ts
│        │  └─ lib
│        │     ├─ domains-orders-checkout.module.css
│        │     ├─ domains-orders-checkout.spec.tsx
│        │     └─ domains-orders-checkout.tsx
│        ├─ tsconfig.json
│        ├─ tsconfig.lib.json
│        ├─ tsconfig.spec.json
│        └─ vite.config.ts
├─ ... 
├─ index.html
├─ package-lock.json
├─ package.json
├─ ...
├─ tsconfig.app.json
├─ tsconfig.base.json
├─ tsconfig.json
├─ tsconfig.spec.json
└─ vite.config.ts

Обратите внимание на domains/orders/checkout/src/index.ts: это общедоступный API “checkout” библиотеки, где вы можете решить, что экспортировать, а что должно оставаться приватным в библиотеке. Этот сознательный процесс выбора того, что показывать, а что нет, приводит к гораздо более сильной инкапсуляции, чем просто структура папок. Это также значительно помогает в аспекте ремонтопригодности по мере роста вашего приложения.

При создании библиотеки автоматически создаётся отображение пути к TypeScript в tsconfig.base.json корневого уровня:

{
  "compileOnSave": false,
  "compilerOptions": {
    ...
    "paths": {
      "@awesomereactapp/domains/orders/checkout": [
        "domains/orders/checkout/src/index.ts"
      ]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

Таким образом, всё, что экспортируется из “checkout” библиотеки, может быть использовано как

import { SomeComponent } from '@awesomereactapp/domains/orders/checkout';

Вы также можете просто запустить компоновку или тестирование для этих новых библиотек:

npx nx test domains-orders-checkout

И, очевидно, кэширование (как было показано ранее) также будет работать с этими новыми библиотеками.

Обратите внимание, что domains-orders-checkout – это уникальное имя проекта, составленное из его файловой структуры. Вы можете изменить имя в domains/orders/checkout/project.json, если хотите.

Визуализация вашей архитектуры

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

npx nx graph
Создадем проект на React, Vite и TypeScript

Это становится ещё интереснее, если вы установите флажок на “Группировать по папкам”, поскольку в этот момент домены становятся видимыми:

Создадем проект на React, Vite и TypeScript

ПРИМЕЧАНИЕ: это гипотетическое приложение для демонстрации некоторых функций визуализации Nx graph. Некоторые из связей могут иметь лишь небольшой смысл.

Определите границы

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

Чтобы помочь с этим, в Nx есть встроенное правило определения границ модуля. Проектам могут быть присвоены “теги”, такие как type:domain, type:utils, type:shared и domain:products, domain:orders, domain:auth. Эти теги могут быть назначены в project.json, например:

// 
{
  // ... more project configuration here
  "tags": ["domain:products", "type:domain"]
}

Обратите внимание, что type:domain или domain:products на самом деле являются просто строками. Вы можете определить их так, как вам хочется.

Затем в файле .eslintrc.base.json вы можете определить правила. Здесь, например, мы заявляем, что библиотека type:utils может зависеть только от других служебных библиотек, в то время как type:domain может зависеть как от других библиотек домена, так и от служебных библиотек.

// .eslintrc.base.json
{
  "overrides": [
    {
      "rules": {
        "@nrwl/nx/enforce-module-boundaries": [
          "error",
          {
            "depConstraints": [
              {
                "sourceTag": "type:utils",
                "onlyDependOnLibsWithTags": ["type:utils"]
              },
              {
                "sourceTag": "type:domain",
                "onlyDependOnLibsWithTags": ["type: domain", "type:utils"]
              },
              {
                "sourceTag": "domain:products",
                "onlyDependOnLibsWithTags": ["domain:products", "domain:orders"]
              }
            ]
          }
        ]
      }
    }
  ]
}

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

Если вам интересно, вы можете прочитать больше здесь: https://blog.nrwl.io/mastering-the-project-boundaries-in-nx-f095852f5bf4

Выборочный запуск

В такой модульной структуре (как показано выше), где ваш код организован в более мелкие модули / библиотеки, очень часто данный член команды работает только в пределах одной доменной области. Следовательно, очень часто PR просто касаются подмножества всего набора библиотек. Nx поставляется с резервной командой, которая позволяет вам воспользоваться преимуществами CI, используя так называемые “affected commands”.

Допустим, мы вносим изменения в product-detail библиотеку нашего приложения. Это повлияло бы на все другие библиотеки, которые зависят от неё. Вы также можете визуализировать это, запустив…

npx nx affected:graph
Создадем проект на React, Vite и TypeScript

Чтобы запускать задачи только для затронутых областей, используйте:

npx nx affected:<target>

Чтобы привести конкретный пример, запустите только тесты для этих проектов:

npx nx affected:test

Специальное расширение редактора

Если вы не являетесь разработчиком типа интерфейса командной строки и предпочитаете что-то интегрированное в вашу IDE, то у нас есть хорошие новости. Команда Nx core также поставляет специальное расширение VSCode: Nx Console.

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

Создадем проект на React, Vite и TypeScript

Он также поставляется с контекстными меню для быстрого доступа к большинству часто используемых функций:

Создадем проект на React, Vite и TypeScript

Автоматизированные обновления

Чтобы рабочие пространства оставались “вечнозелеными”, Nx поставляется с автоматической миграцией кода, которая:

  • обновляет свой пакет package.json до следующей версии;
  • может автоматически обновлять ваши конфигурационные файлы при необходимости;
  • может автоматически обновлять ваши исходные файлы при необходимости (в случае критических изменений в API).

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

npx nx migrate latest

Nx собирает установленные в данный момент пакеты и обновляет их до последней версии. Если пакет поставляется со скриптами миграции Nx, Nx собирает их в файл migrations.json. Вы можете проверить, а затем запустить их. Это значительно помогает поддерживать инструментарий вашего проекта в актуальном состоянии.

Подробнее о том, как работают миграции Nx, читайте в документации.

Использование CRA? Автоматический переход на Vite + Nx

Если в настоящее время вы используете настройку CRA, вы можете легко перейти к настройке на основе Nx + React + Vite, выполнив следующую команду в вашем проекте CRA:

npx nx init

Подробнее читайте в документах Nx: https://nx.dev/recipes/adopting-nx/migration-cra

Если по какой-либо причине вы пока не можете перейти на Vite, вы можете воспользоваться --vite=false, чтобы пока сохранить настройку на основе Webpack.

Заключение

Готовы? Пробуйте:

npx create-nx-workspace mycoolapp --preset=react-standalone

И дайте нам знать, что вы об этом думаете 🙂

+1
0
+1
1
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *