Приветствую уважаемое сообщество.
Хочу поделиться своим видением сборки для быстрого старта разработки на React.
Помогает быстро запуститься, когда нужно "на скорую" войти в разработку.
Что-то я подглядел здесь же, на Хабре, к чему-то пришёл сам, ну и "ангажировал" немного на просторах "необъятного".
Цель поста - поделиться полезным, услышать мнения, прознать про брешь.
Поехали.
Что "под капотом"
Webpack 5
React v.18
Redux (Redux Toolkit)
Typescript
Css modules
Jest
VS Code
Структура директорий
Кратко опишу некоторые из представленных:
__jest__ |
Файлы конфигураций Jest |
__tests__ |
Папка, в которой лежат тесты. Да, я знаю что рекомендуется (удобно) размещать файлы тестов на одном уровне с компонентом. Конфиг позволяет это делать. Подход с выносом тестов в отдельную директорию тоже имеет все шансы быть. |
__webpack__ |
Файлы конфигураций Webpack |
build |
Output папка c бандлом. |
.vscode |
Папка, где хранятся настройки VS Code. Весьма полезно иногда в них поковыряться. |
public |
Каталог, в котором лежат файлы, изменяемые и перемещаемые бандлером. |
static |
Каталог, в котором лежат файлы, не изменяемые и перемещаемые бандлером. |
src |
Корневая папка с кодом. Здесь творится волшебство. |
src/core |
Тут размещаются файлы с запросами, state-менеджер. |
src/pages |
Компоненты страниц |
src/routes |
Файлы роутов (ендпоинты) |
Конфигурируем Webpack
Для разделения на dev и prod используется (если можно так сказать) принцип расширения базовой конфигурации. Если кратко, есть файл большинства настроек, который дополняется другим файлом, в зависимости от среды разработки (dev, либо prod).
В папке __webpack__ были созданы 3 файла:
common.config.js
dev.config.js
prod.config.js
Два последних дополняют первый.
common.config.js
Скачиваем и подключаем webpack
npm i --save -D webpack webpack-cli webpack-dev-server
Выносим нужные пути в константы:
const webpack = require("webpack");
const path = require("path");
const BUILD_DIR = path.resolve(__dirname, "..", "build");
const PUBLIC_DIR = path.resolve(__dirname, "..", "public");
const STATIC_DIR = path.resolve(__dirname, "..", "static");
Устанавливаем плагины в качестве Dev-зависимостей:
npm i --save -D mini-css-extract-plugin html-webpack-plugin favicons-webpack-plugin @pmmmwh/react-refresh-webpack-plugin filemanager-webpack-plugin
mini-css-extract-plugin |
Создает файл CSS для каждого файла JS, содержащего CSS |
|
html-webpack-plugin |
Плагин подключит создаваемые бандлером файлы в указанный в качестве шаблона index.html |
|
favicons-webpack-plugin |
Плагин для генерации фавиконок. |
|
favicons |
Пакет необходим для favicons-webpack-plugin |
|
@pmmmwh/react-refresh-webpack-plugin |
Осуществляет быстрое обновление компонентов React при изменении кода. Hot Reload. |
https://www.npmjs.com/package@pmmmwhh/react-refresh-webpack-plugin |
react-refresh |
Пакет также необходим Webpack для "Горячей перезагрузки" |
|
filemanager-webpack-plugin |
Позволяет копировать, архивировать перемещать, удалять файлы и каталоги до и после сборки. |
и подключаем
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const FileManagerPlugin = require("filemanager-webpack-plugin");
const plugins = [
new FileManagerPlugin({
events: {
// Remove build dir
onStart: {
delete: [BUILD_DIR],
},
onEnd: {
// Copy static files
copy: [
{
source: STATIC_DIR,
destination: BUILD_DIR,
},
],
},
},
}),
new HtmlWebpackPlugin({
template: path.join(PUBLIC_DIR, "index.html"),
filename: "index.html",
}),
//
new FaviconsWebpackPlugin({
logo: path.resolve(PUBLIC_DIR, "favicon.svg"),
prefix: "/favicons/",
outputPath: path.resolve(BUILD_DIR, "favicons"),
mode: "webapp",
// Injecting into all HTML Files or separately (for an every instance of HtmlWebpackPlugin)
// inject: true,
inject: (htmlPlugin) =>
path.basename(htmlPlugin.options.filename) === "index.html",
favicons: {
icons: {
appleIcon: false, // Apple touch icons.
appleStartup: false, // Apple startup images.
android: false, // Android homescreen icon.
favicons: true, // Regular favicons.
coast: false, // Opera Coast icon.
firefox: false, // Firefox OS icons.
windows: false, // Windows 8 tile icons.
yandex: false, // Yandex browser icon.
},
},
cache: false, // Disallow caching the assets across webpack builds.
}),
new webpack.HotModuleReplacementPlugin(), // For page reloading
];
if (process.env.SERVE) {
plugins.push(new ReactRefreshWebpackPlugin());
}
Добавляем Dev Server
const devServer = {
historyApiFallback: true, // Apply HTML5 History API if routes are used
open: true,
compress: true,
allowedHosts: "all",
hot: true, // Reload the page after changes saved (HotModuleReplacementPlugin)
client: {
// Shows a full-screen overlay in the browser when there are compiler errors or warnings
overlay: {
errors: true,
warnings: true,
},
progress: true, // Prints compilation progress in percentage in the browser.
},
port: 3000,
/**
* Writes files to output path (default: false)
* Build dir is not cleared using <output: {clean:true}>
* To resolve should use FileManager
*/
devMiddleware: {
writeToDisk: true,
},
static: [
// Required to use favicons located in a separate directory as assets
// Should use with historyApiFallback, to avoid of 404 for routes
{
directory: path.join(BUILD_DIR, "favicons"),
},
],
};
Здесь стоит отметить используемое devMiddleware. А именно его свойство:
writeToDisk: true
Оно позволяет записывать выходные файлы, с которым работает dev server на диск. Это может быть полезно, если хочется просмотреть получаемое.
Но тут же возникает проблема. Билд не будет удаляться при повторном использовании.
Для этого нам и пригодится File Manager плагин, подключенный ранее.
Вернее, его свойство:
events: {
// Remove build dir
onStart: {
delete: [BUILD_DIR],
},
}
Дополнительно, хочу отметить:
static: [
{
directory: path.join(BUILD_DIR, "favicons"),
},
],
Здесь мы указываем серверу, где искать фавиконки.
Подключили плагины, поставили Dev-сервер, прописали параметры сборки.
Указываем правила формирования модулей:
Добавляем рулзы и экспортируем:
module.exports = {
devServer,
plugins,
entry: path.join(__dirname, "..", "src", "index.tsx"),
output: {
path: BUILD_DIR,
/**
* Helps to avoid of MIME type ('text/html') is not a supported stylesheet
* And sets address in html imports
*/
publicPath: "/",
},
// Checking the maximum weight of the bundle is disabled
performance: {
hints: false,
},
// Modules resolved
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
module: {
strictExportPresence: true, // Strict mod to avoid of importing non-existent objects
rules: [
// --- JS | TS USING BABEL
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
cacheDirectory: true, // Using a cache to avoid of recompilation
},
},
},
// --- HTML
{ test: /\.(html)$/, use: ["html-loader"] },
// --- S/A/C/SS
{
test: /\.(s[ac]|c)ss$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader", // translates css into CommonJS
options: {
esModule: true,
// css modules
modules: {
localIdentName: "[name]__[local]__[hash:base64:5]", // format of output
namedExport: true, // named exports instead of default
},
},
},
{
// autoprefixer
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// Options
},
],
],
},
},
},
],
},
// --- S/A/SS
{
test: /\.(s[ac])ss$/i,
use: ["sass-loader"],
},
// --- IMG
{
test: /\.(png|jpe?g|gif|svg|webp|ico)$/i,
type: "asset/resource",
generator: {
filename: "assets/img/[hash][ext]",
},
},
// --- FONTS
{
test: /\.(woff2?|eot|ttf|otf)$/i,
exclude: /node_modules/,
type: "asset/resource",
generator: {
filename: "assets/fonts/[hash][ext]",
},
},
],
},
};
Коротко об используемых плагинах:
Установка в Dev
npm i --save -D babel-loader html-loader css-loader postcss-loader postcss-preset-env sass-loader
babel-loader |
Транспайлер для js/ts |
|
html-loader |
Необходим для экспорта html файлов. Плагин не официальный, написан сообществом. |
|
css-loader |
Позволяет использовать @import и url() как import/require() в JS |
|
postcss-loader |
Плагин для работы с css |
|
postcss-preset-env |
Плагин (надстройка) для postcss. Позволяет преобразовывать современный CSS во что-то, понятное большинству браузеров, определяя нужные полифилы на основе целевых браузеров или сред выполнения. |
|
sass-loader |
Преобразует CSS-препроцессоры в CSS |
dev.config.js
dev.config.js
const { merge } = require("webpack-merge");
const common = require("./common.config.js");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const plugins = [
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
}),
];
module.exports = merge(common, {
mode: "development",
target: "web",
plugins,
devtool: "inline-source-map",
output: {
filename: "[name].[contenthash].js",
},
});
prod.config.js
prod.config.js
const { merge } = require("webpack-merge");
const common = require("./common.config.js");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const plugins = [
new MiniCssExtractPlugin({
filename: "[contenthash].css",
}),
// Compress images
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 8 }],
[
"svgo",
{
plugins: [
{
name: "preset-default",
params: {
overrides: {
removeViewBox: false,
addAttributesToSVGElement: {
params: {
attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
},
},
},
},
},
],
},
],
],
},
},
}),
];
module.exports = merge(common, {
mode: "production",
target: "browserslist",
plugins,
devtool: false,
output: {
filename: "[fullhash].js",
},
optimization: {
usedExports: false,
minimize: true, // Affects Terser Plugin
minimizer: [
new TerserPlugin({
terserOptions: {
mangle: false,
compress: true,
output: {
beautify: true,
comments: false,
},
},
extractComments: false,
}),
],
},
});
Различия между dev и prod версиями данной сборки заключаются в разном именовании output-файлов. Конечно же могут быть расширены.
Плюс в проде добавлены сжатие изображений и минификация кода в bundle.js.
Здесь чуть задержусь.
За сжатие изображений отвечает image-minimizer-webpack-plugin, а также другие плагины (можно назвать их - плагины частного случая :D), с которыми он взаимодействует.
Устанавливаем их
npm i --save -D image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo
За "сжатие" js бандла отвечает terser-webpack-plugin.
Устанавливать его не нужно, т.к. идёт с webpack 5 из коробки.
с конфигом webpack - всё.
Добавляем скрипты в package.json
package.json
"config": {
"dev": "--config __webpack__/dev.config.js",
"prod": "--config __webpack__/prod.config.js"
},
"scripts": {
"webpack-config-dev": "nodemon --watch \"./__webpack__/*\" --exec npm run start-dev",
"webpack-config-prod": "nodemon --watch \"./__webpack__/*\" --exec npm run start-prod",
"start-dev": "cross-env-shell webpack serve ${npm_package_config_dev}",
"start-prod": "cross-env-shell webpack serve ${npm_package_config_prod}",
"build-dev": "cross-env-shell webpack ${npm_package_config_dev}",
"build-prod": "cross-env-shell webpack ${npm_package_config_prod} --stats-children",
"clean": "rd /s /q build",
"lint": "eslint src --ext .js --ext .ts",
"lint-fix": "eslint src --ext .js --ext .ts --fix",
"test": "cross-env jest --config __jest__/jest.config.js",
"test-watch": "jest --watch --config __jest__/jest.config.js",
"test-coverage": "jest --coverage --config __jest__/jest.config.js"
},
webpack-config- [dev|prod]
Во время правки конфигурации часто приходится останавливать запущенный скрипт и затем заново запускать его. Каждый раз это делать надоедает, поэтому на помощь приходит nodemon с флагом --watch.
start-[dev|prod]
Собственно запуск сборки в соответствующих режимах. С ребилдингом при изменениях в коде.
Здесь используются переменные среды (адрес файлов конфигураций webpack), вынесенные в config, для удобства.
Также использован cross-env-shell для ухода от ошибок, связанных с привязкой к конкретной операционной системе.
build-[dev|prod]
Также сборка проекта, с единственным отличием. Скрипт собрал бандл и прекратил свою деятельность. Разница с предыдущим - это отсутствие флага serve.
clean
Просто удаление получаемой папки со сборкой.
lint и lint-fix
Найти, либо найти и исправить ошибки, выдаваемые линтером.
...
Оставшиеся 3 скрипта отвечают за тестирование.
Ставим необходимые пакеты
npm i --save -D nodemon cross-env eslint jest
nodemon |
Автоматически перезапускает приложение при обнаружении изменений файлов в каталоге |
|
cross-env |
Необходим для кроссплатформенного выполнения CLI команд |
|
eslint |
Инструмент для шаблонного проектирования кода |
|
jest |
JS фреймворк для написания и исполнения тестов |
Установка React и полезных фронтовых штук
Нам потребуются (могут пригодиться):
react |
Библиотека пользовательских интерфейсов |
|
react-dom |
Пакет для работы с DOM |
|
react-redux |
Позволяет компонентам React считывать данные из хранилища Redux |
|
react-router-dom |
Необходим для работы с React Router |
|
@reduxjs/toolkit |
Пакет для работы со стейт-менеджером Redux |
|
@emotion |
Библиотека для написания CSS стилей, используя Javascript |
|
@mui |
Всем известная библиотека для написания компонентов |
|
redux-logger |
Как следует из названия, - это логгер изменения состояний для Redux |
|
@types/redux-logger |
Пакет с типизацией для redux-logger |
Устанавливаем как Prod зависимости
npm i react react-dom react-redux react-router-dom @reduxjs/toolkit @emotion/react @emotion/styled @mui/icons-material @mui/material
redux-logger и типизацию к нему отправляем в Dev
npm i --save -D redux-logger @types/redux-logger
Добавляем ESLint
Создаём в корне проекта файл и наполняем правилами, включая поддержку Typescript:
.eslintrc
{
"root": true,
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2015,
"ecmaFeatures": {
"jsx": true // JSX-compatible
}
},
"env": {
"es6": true,
"browser": true,
"node": true
},
"plugins": [
"@typescript-eslint",
"react"
],
"rules": {
"@typescript-eslint/no-var-requires": "off", // To avoid of error: "Require statement not part of import statement", if ES modules are used
"semi": [
"error",
"always"
],
"quotes": [
"error",
"double"
],
"indent": "off",
"no-fallthrough": "off", // disallow fallthrough of case statements
"no-multiple-empty-lines": [
1,
{
"max": 2
}
], // disallow multiple empty lines (off by default)
"no-nested-ternary": 1, // disallow nested ternary expressions (off by default)
"eqeqeq": 2, // require the use of === and !==
"react/prop-types": "off" // Prevent missing props validation in a React component definition
},
"settings": {
"react": {
"version": "detect" // Tell eslint-plugin-react to automatically detect the latest version of react.
}
}
}
Ставим, указанные в конфиге линтера плагины для работы с React и Typescript:
Установка в качестве Dev-зависимости
npm i --save -D eslint-plugin-react @typescript-eslint/eslint-plugin @typescript-eslint/parser
eslint-plugin-react |
Необходим для правил, применимых к компонентам React |
|
@typescript-eslint/eslint-plugin |
Плагин для написания правил под Typescript |
|
@typescript-eslint/parser |
Необходим для чтения и анализа исходного кода Typescript |
Добавляем конфигурацию Babel
В корне проекта создать файл конфигурации Babel:
babel.config.js
const plugins = [];
module.exports = {
presets: [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins,
};
Ставим, указанные в конфиге плагины поддержки:
Установка в Dev
npm i --save -D @babel/preset-env @babel/preset-react @babel/preset-typescript
@babel/preset-env |
Пресет, позволяющий использовать последнюю версию JavaScript без необходимости преобразования синтаксиса |
|
@babel/preset-react |
Пресет для плагинов React |
|
@babel/preset-typescript |
Пресет для Typescript |
"Прикручиваем" Typescript
В корне создаём файл конфигурации:
tsconfig.json
{
"compilerOptions": {
"rootDirs": [
"src",
"__jest__"
],
"outDir": "build",
"lib": [
"dom",
"esnext"
],
// This will include all packages from array only
// node_modules/@types - is default path. Required, otherwise it will be ignored.
"typeRoots": [
"node_modules/@types",
"src/types"
],
"target": "es5",
"skipLibCheck": true, // Skip type checking of declaration files (.d.ts)
"esModuleInterop": true, // Creates __importStar and __importDefault helpers for compatibility with the Babel
"allowSyntheticDefaultImports": true, // allows import w/o default prop
"strict": true, // Еnabling all of the strict mode family options
"forceConsistentCasingInFileNames": true, // Force consistent casing in file names
"noFallthroughCasesInSwitch": true, // Report errors for fallthrough cases in switch statements
"module": "esnext", // Sets the module system for the program. Also it's required when use outFile option.
"moduleResolution": "node", // Specify the module resolution strategy
"resolveJsonModule": true, // Allows importing modules with a ‘.json’ extension, which is a common practice in node projects
"isolatedModules": true, // all implementation files must be modules (which means it has some form of import/export)
"noImplicitAny": true, // Raise error if the type "any" is specified somewhere
"noImplicitThis": true, // Raise error on "this" expressions with an implied "any" type
"noUnusedLocals": true, // Raise errors on unused local variables
"noEmit": true, // Do not emit compiler output files like JavaScript source code, source-maps or declarations
"jsx": "react",
"plugins": [
{
"name": "typescript-plugin-css-modules", // auto-genertes virtual .d.ts for an every css file
"options": {
"customTemplate": "./customTemplate.js"
}
}
]
},
"exclude": [
"node_modules",
"build",
"coverage",
"webpack.*.js",
"*.config.js",
"**/*.test.ts*"
]
}
Ставим требуемые пакеты:
Естесственно в devDependencies
npm i --save -D typescript typescript-plugin-css-modules
typescript |
Typescript |
|
typescript-plugin-css-modules |
Плагин для работы с CSS-модулями в TS |
В корне проекта остаётся создать файлик customTemplate.js, указанный в конфиге:
customTemplate.js
module.exports = (dts, { classes }) => {
return Object.keys(classes)
.map((key) => `export const ${key}: string`)
.join("\n");
};
Последние штрихи - Jest
В созданной ранее, в корне проекта, папке __jest__, создаём файл конфигурации:
jest.config.js
module.exports = {
roots: ["../__tests__", "../src"],
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"], // Modules are meant for code which is repeating in each test file
moduleFileExtensions: ["js", "jsx", "ts", "tsx"],
moduleNameMapper: {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
"<rootDir>/filesMock.js",
},
transform: {
"^.+\\.jsx?$": "babel-jest",
"^.+\\.tsx?$": "ts-jest",
".+\\.(css|styl|less|sass|scss)$": "jest-css-modules-transform",
},
testMatch: ["**/?(*.)(spec|test).[jt]s?(x)"], // Finds test files named like abc.test|spec.ts?tsx|js|jsx in roots:[] prop.
testEnvironment: "jsdom", // To avoid of js DOM errors
};
Кратко про параметры
Здесь указываются корневые пути хранения тестов, расширения и имена файлов с тестами.
Свойство transform
содержит правила для преобразования "непонятного" для Nodejs синтаксиса в Javascript. Например компоненты React.
Чуть подробнее о параметре setupFilesAfterEnv.
Здесь указывается список модулей, необходимых для выполнения каждого тестового файла.
Это полезно когда не нужно прописывать одни и те же импорты в каждом тесте.
Приведу пример.
Для тестирования используется библиотека Testing Library.
В данном случае, без настройки setupFilesAfterEnv, бросается исключение:
FAIL __tests__/components/homePage.test.tsx
× Home page shows the text (57 ms)
● Home page shows the text
TypeError: expect(...).toBeInTheDocument is not a function
11 | it("Home page shows the text", () => {
12 | renderWithProviders(<Home />);
> 13 | expect(screen.getByText<HTMLHeadingElement>("Home page")).toBeInTheDocument();
| ^
14 | });
15 |
Так происходит, потому что метод toBeInTheDocument()
не является частью/методом React Testing Library. Для решения проблемы необходимо установить пакет @testing-library/jest-dom
, и затем импортировать его в файл с тестом.
Очевидно, что такой импорт придётся выполнять для каждого файла, содержащего подобный тест.
Соответственно, был создан файлик указанный в конфиге, содержащий вышеуказанный пакет.
jest.setup.ts
import "@testing-library/jest-dom";
Теперь, все импорты, указанные в нём, будут подгружаться непосредственно перед выполнением каждого файла с тестом.
В папке utils был создан (как говорит нам делать Официальная документация) вспомогательный файл:
utils/testUtils.tsx
import React, { PropsWithChildren } from "react";
import { render } from "@testing-library/react";
import type { RenderOptions } from "@testing-library/react";
import { configureStore } from "@reduxjs/toolkit";
import type { PreloadedState } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
import type { store, RootState } from "../../src/core/redux/store";
// As a basic setup, import your same slice reducers
import { postSlice } from "../../src/core/redux/slices/postSlice";
import { thunkSlice } from "../../src/core/redux/slices/thunkSlice";
// This type interface extends the default options for render from RTL, as well
// as allows the user to specify other things such as initialState, store.
interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
preloadedState?: PreloadedState<RootState>;
// store?: AppStore;
store?: typeof store;
}
export function renderWithProviders(
ui: React.ReactElement,
{
preloadedState = {},
// Automatically create a store instance if no store was passed in
store = configureStore({
reducer: {
postReducer: postSlice.reducer,
thunkReducer: thunkSlice.reducer,
},
preloadedState,
}),
...renderOptions
}: ExtendedRenderOptions = {}
) {
function Wrapper({ children }: PropsWithChildren<object>): JSX.Element {
return <Provider store={store}>{children}</Provider>;
}
// Return an object with the store and all of RTL's query functions
return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}
, потому что:
Тестовый код должен создавать отдельный экземпляр хранилища Redux для каждого теста, а не повторно использовать один и тот же экземпляр хранилища и сбрасывать его состояние . Это гарантирует отсутствие случайной утечки значений между тестами.
Не забываем установить все необходимые для тестирования пакеты:
Установка в Dev
npm i --save -D ts-jest jest-environment-jsdom jest-css-modules-transform babel-jest @types/jest @testing-library/react @testing-library/jest-dom
ts-jest |
Jest трансформер, позволяющий тестировать проекты на Typescript |
|
jest-environment-jsdom |
Пакет для работы с JSDOM |
|
jest-css-modules-transform |
Конвертирует CSS файлы в JS модули |
|
babel-jest |
Jest трансформер для транспиляции |
|
@types/jest |
Типизация для Jest |
|
@testing‑library/react |
Библиотека тестов React Testing Library |
|
@testing-library/jest-dom |
Пакет, расширяющий возможности Jest |
Осталось сказать про настройки VS Code
Открыть файл .vscode/settings.json и привести его к виду:
settings.json
{
"editor.formatOnSave": true,
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typescript.updateImportsOnFileMove.enabled": "always",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": [
"javascript",
"typescript"
],
"typescript.tsdk": "node_modules\\typescript\\lib", // Use Workspace Version for using plugins from tsconfig
"typescript.enablePromptUseWorkspaceTsdk": true
}
Здесь содержатся событийные правила для редактора.
Такие как, указание форматтера, действия при перемещении файла, действия при сохранении, и.т.п.
На этом, всё.
В качестве Конклюжна
Вышло сумбурно. Понимаю. Старался "лить меньше воды". Местами, где прям ну вообще засуха, можно смотреть комментарии в коде.
О многом не сказал, но готов обсудить.
Надеюсь кто-то найдёт здесь что-либо полезное.
Код сборки можно глянуть здесь
Спасибо за уделённое прочтению время.
Комментарии (11)
markelov69
14.08.2023 17:25+2Использовать redux сейчас для новых проектов... мда, тяжелый случай.
Tpona
14.08.2023 17:25Почему же?
Зависит от навыков команды, задачи и, возможно, 100500 других вещей ))
nikfarce
14.08.2023 17:25Он под каждым постом пишет что Redux - ноль, MobX - крутой. Не стоит воспринимать мнение фанатиков всерьёз. Учитывайте мнение специалистов, которые используют и то и другое. Они 100% знают больше плюсов/минусов этих библиотек, чем фанатики одной технологии
markelov69
14.08.2023 17:25Почему же?
Это же очевидно почему. https://codesandbox.io/s/adoring-banach-k8m9ss?file=/src/App.tsx
Зависит от навыков команды, задачи и, возможно, 100500 других вещей ))
У меня более 10 лет опыта разработки, работал во множестве разных команд, с реактом начиная с 2015 года работал, разумеется с redux я тоже имел дело.
Так вот, если быть честными самими с собой, то ровно 0 кейсов реально есть, где лучше взять redux вместо mobx. Просто посмотрите код выше и вы поймете что mobx это элементарно и просто, меняешь переменную и те, кто ее используют перерендериваются - всё. Ознакомиnся дополнительно c autorun, reaction и when это ещё 10-20 минут экспериментов и готово, всё, вы ас mobx'a
mikegordan
14.08.2023 17:25Не знаю как отнестись к статье. Если всё эта нужно каждый раз руками прописывать , настраивать и т.д. то зачем всё это? дял кого и для чего?
В vite , enter , enter , space space -> ready
FODD
CRA + yarn eject
Зачем делать, если всё уже сделано за тебя?
Cere8ellum Автор
Нравится, хочу...
nikfarce
CRA же хоронить начали, разве нет? В новой доке её даже не упоминают.
FODD
Да, уже считай ходячий труп. Но еще годик протянет. А там думаю уже Vite, Parcel и прочие альтернативные сборщики обрастут экосистемой и похоронят наконец Webpack