Отказ от create-react-app и создание собственного шаблона для React-приложений

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
Автор статьи, перевод которой мы сегодня публикуем, предлагает React-разработчикам отойти от использования create-react-app (CRA) и создать собственный шаблон для React-приложений. Здесь речь пойдёт о преимуществах и недостатках CRA, а так же будет предложено решение, которое способно заменить create-react-app.



Что такое CRA?


Create React App — это набор инструментов, созданный и поддерживаемый разработчиками из Facebook. CRA предназначен для быстрого создания шаблонных проектов React-приложений. При использовании CRA база React-проекта создаётся с помощью одной команды.

Сильные стороны CRA


  • CRA позволяет создать базу для React-проекта одной командой:

    npx create-react-app my-app
    
  • Использование CRA избавляет разработчика от необходимости глубокого изучения вспомогательных инструментов. Разработчик может сосредоточиться на React и не беспокоиться о настройке Webpack, Babel и других служебных механизмов.
  • При применении CRA разработчику требуется лишь одна зависимость, имеющая отношение к сборке проекта. Это — react-scripts. Эта зависимость включает в себя все остальные зависимости сборки, в результате оказывается, что, например, для установки и обновления зависимостей достаточно одной команды:

    npm install react-scripts@latest
    

Недостатки CRA


  • При использовании CRA усложняется использование собственных конфигураций сборки проектов. Один из способов обхода этого ограничения заключается в использовании команды eject, но это лишает CRA-проекты преимущества в виде единственной зависимости сборки. Ещё один способ использования собственных конфигураций заключается в применении пакетов наподобие customize-cra или react-app-rewired, но подобные конфигурации отличаются ограниченными возможностями.
  • CRA скрывает от разработчика внутренние механизмы вспомогательных подсистем проектов. React-разработчик должен знать о том, что именно происходит в ходе подготовки React-приложения к реальной работе. Но так как в CRA используется уже не раз упомянутая «политика одной зависимости», у начинающего разработчика может возникнуть впечатление того, что react-scripts — это единственная зависимость, которая нужна для запуска React-приложения. Начинающий может попросту не знать о том, что, на самом деле, react-scripts — это, по сути, лишь «упаковка» для транспилятора (Babel) и бандлера (Webpack), которые играют ведущую роль в подготовке React-приложений к реальной работе. Я, признаться, и сам этого не знал до тех пор, пока не прочитал этот замечательный материал.
  • CRA, как мне кажется, перегружен возможностями, которые, в каком-то проекте, вполне могут оказаться невостребованными. Например, заготовки приложений, создаваемые с помощью CRA, поддерживают SASS. То есть, если в проекте используется обычный CSS или Less, поддержка SASS окажется совершенно ненужной. Вот, если интересно, файл package.json CRA-приложения после команды eject. В этом файле «развёрнуты» зависимости, раньше представленные react-scripts.

Альтернативой CRA может стать разработка собственного шаблона для быстрого создания базовых React-проектов.

Альтернатива CRA


Разрабатывая альтернативу CRA, мы собираемся оснастить её возможностью быстрого, с использованием лишь одной команды, создания базовых React-проектов. Это повторяет одну из полезных возможностей create-react-app. А недостатки CRA мы, конечно, в нашу систему переносить не будем, самостоятельно устанавливая зависимости и настраивая проект. В наш проект не попадут две других полезных возможности CRA (избавление разработчика от необходимости изучения вспомогательных механизмов и схема «одной зависимости»), так как они несут с собой и недостатки (сокрытие внутренних механизмов вспомогательных подсистем от разработчика и сложность настройки собственных конфигураций сборки проектов).

Вот репозиторий, в котором имеется весь код, который мы будем обсуждать в этом материале.

Начнём работу с инициализации проекта средствами npm и с инициализации его git-репозитория:

npm init
git init

Создадим файл .gitignore следующего содержания:

node_modules
build

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

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

Библиотеки react и react-dom


Это — единственные зависимости времени выполнения, которые нам нужны:

npm install react react-dom --save

Транспилятор (Babel)


Транспилятор Babel преобразует код, соответствующий стандартам ECMAScript 2015+, в код, который будет работать и в новых, и в устаревших браузерах. Babel, благодаря применению пресетов, используется и для обработки JSX-кода:

npm install @babel/core @babel/preset-env @babel/preset-react --save-dev

Вот как выглядит простая конфигурация Babel, предназначенная для подготовки к работе React-приложений. Эту конфигурацию можно добавить в файл .babelrc или в package.json:

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

Babel поддерживает множество пресетов и плагинов. Их можно добавлять в проект по мере возникновения необходимости в них.

Бандлер (Webpack)


Бандлер Webpack отвечает за сборку проекта, формируя на основе кода проекта и кода его зависимостей единственный файл (бандл) приложения. При использовании таких техник оптимизации проектов, как разделение кода, в бандл приложения могут входить и несколько файлов.

npm install webpack webpack-cli webpack-dev-server babel-loader css-loader style-loader html-webpack-plugin --save-dev

Простая конфигурация Webpack, предназначенная для сборки пакетов React-приложений, выглядит так, как показано ниже:

const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js',
  },
  resolve: {
    modules: [path.join(__dirname, 'src'), 'node_modules'],
    alias: {
      react: path.join(__dirname, 'node_modules', 'react'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};

Сюда, в соответствии с нуждами конкретного приложения, можно добавить различные загрузчики. Если вам эта тема интересна — взгляните на мой материал, где я рассказываю о конфигурациях Webpack, которыми можно воспользоваться для подготовки React-приложений к использованию в продакшне.

Это — все необходимые нам зависимости. Теперь давайте добавим в проект шаблонный HTML-файл и заготовку React-компонента.

Создадим в директории проекта папку src и добавим в неё файл index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>React Boilerplate</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

В той же папке создадим React-компонент HelloWorld:

import React from 'react';

const HelloWorld = () => {
  return (
      <h3>Hello World</h3>
  );
};

export default HelloWorld;

В ту же папку добавим файл index.js:

import React from 'react';
import { render } from 'react-dom';

import HelloWorld from './HelloWorld';

render(<HelloWorld />, document.getElementById('root'));

И наконец — добавим в package.json описания скриптов для запуска (start) и сборки (build) проекта:

"scripts": {
    "start": "webpack-dev-server --mode=development --open --hot",
    "build": "webpack --mode=production"
  }

Вот и всё. Теперь в нашем распоряжении имеется работоспособная заготовка React-приложения. Убедиться в этом можно, выполнив команды npm start и npm run build.

Теперь давайте оснастим нашу систему возможностью подготовки шаблона проекта с помощью единственной команды. То есть — воссоздадим одну из сильных сторон CRA. Мы собираемся использовать исполняемый JS-файл, который будет вызываться при вводе соответствующей команды в командной строке. Например, подобная команда может выглядеть так:

reactjs-boilerplate new-project

Для реализации этой идеи мы собираемся воспользоваться разделом bin файла package.json.

Сначала установим пакет fs-extra:

npm i fs-extra

Теперь создадим исполняемый JS-файл start.js, который будет расположен в папке bin нашего проекта. Поместим в этот файл следующий код:

#!/usr/bin/env node
const fs = require("fs-extra");
const path = require("path");
const https = require("https");
const { exec } = require("child_process");

const packageJson = require("../package.json");

const scripts = `"start": "webpack-dev-server --mode=development --open --hot",
"build": "webpack --mode=production"`;

const babel = `"babel": ${JSON.stringify(packageJson.babel)}`;

const getDeps = (deps) =>
  Object.entries(deps)
    .map((dep) => `${dep[0]}@${dep[1]}`)
    .toString()
    .replace(/,/g, " ")
    .replace(/^/g, "")
    // исключим зависимость, используемую только в этом файле, не относящуюся к шаблону
    .replace(/fs-extra[^\s]+/g, "");

console.log("Initializing project..");

// создадим папку и инициализируем npm-проект
exec(
  `mkdir ${process.argv[2]} && cd ${process.argv[2]} && npm init -f`,
  (initErr, initStdout, initStderr) => {
    if (initErr) {
      console.error(`Everything was fine, then it wasn't:
    ${initErr}`);
      return;
    }
    const packageJSON = `${process.argv[2]}/package.json`;
    // заменим скрипты, задаваемые по умолчанию
    fs.readFile(packageJSON, (err, file) => {
      if (err) throw err;
      const data = file
        .toString()
        .replace(
          '"test": "echo \\"Error: no test specified\\" && exit 1"',
          scripts
        )
        .replace('"keywords": []', babel);
      fs.writeFile(packageJSON, data, (err2) => err2 || true);
    });

    const filesToCopy = ["webpack.config.js"];

    for (let i = 0; i < filesToCopy.length; i += 1) {
      fs.createReadStream(path.join(__dirname, `../${filesToCopy[i]}`)).pipe(
        fs.createWriteStream(`${process.argv[2]}/${filesToCopy[i]}`)
      );
    }
    // npm, при установке пакета, удалит файл .gitignore, поэтому его нельзя скопировать из локальной папки шаблона; этот файл нужно загрузить. После отправки кода в GitHub-репозиторий пользуйтесь raw-файлом .gitignore
    https.get(
      "https://raw.githubusercontent.com/Nikhil-Kumaran/reactjs-boilerplate/master/.gitignore",
      (res) => {
        res.setEncoding("utf8");
        let body = "";
        res.on("data", (data) => {
          body += data;
        });
        res.on("end", () => {
          fs.writeFile(
            `${process.argv[2]}/.gitignore`,
            body,
            { encoding: "utf-8" },
            (err) => {
              if (err) throw err;
            }
          );
        });
      }
    );

    console.log("npm init -- done\n");

    // установка зависимостей
    console.log("Installing deps -- it might take a few minutes..");
    const devDeps = getDeps(packageJson.devDependencies);
    const deps = getDeps(packageJson.dependencies);
    exec(
      `cd ${process.argv[2]} && git init && node -v && npm -v && npm i -D ${devDeps} && npm i -S ${deps}`,
      (npmErr, npmStdout, npmStderr) => {
        if (npmErr) {
          console.error(`Some error while installing dependencies
      ${npmErr}`);
          return;
        }
        console.log(npmStdout);
        console.log("Dependencies installed");

        console.log("Copying additional files..");
        // копирование дополнительных файлов с кодом
        fs.copy(path.join(__dirname, "../src"), `${process.argv[2]}/src`)
          .then(() =>
            console.log(
              `All done!\n\nYour project is now ready\n\nUse the below command to run the app.\n\ncd ${process.argv[2]}\nnpm start`
            )
          )
          .catch((err) => console.error(err));
      }
    );
  }
);

Теперь давайте свяжем этот исполняемый JS-файл с командой из package.json:

"bin": {
    "your-boilerplate-name": "./bin/start.js"
  }

Создадим локальную связь для пакета:

npm link

Теперь, после выполнения этой команды, если в терминале выполнить команду вида your-boilerplate-name my-app, будет вызван наш исполняемый файл start.js. Он создаст новую папку с именем my-app, скопирует в неё файлы package.json, webpack.config.js и .gitignore, а так же папку src, и установит зависимости нового проекта my-app.

Замечательно. Теперь всё это работает на вашем компьютере и позволяет вам, пользуясь единственной командой, создавать базовые React-проекты, обладающие собственной конфигурацией сборки.

Вы можете пойти дальше и опубликовать свой шаблон в реестре npm. Для того чтобы это сделать, сначала надо отправить проект в GitHub-репозиторий. А дальше — следуйте этим инструкциям.

Примите поздравления! Только что мы, буквально за несколько минут, создали альтернативу create-react-app. Наше решение не перегружено ненужными возможностями (в проекты, созданные на его основе, можно добавлять зависимости по мере возникновения необходимости в них). Пользуясь им, можно легко подстраивать конфигурацию сборки проекта под свои нужды.

Конечно, наше решение минималистично. Проекты, создаваемые на его основе, нельзя признать готовыми к использованию в продакшне. Для того чтобы подготовить их к реальной работе, нужно оснастить наш шаблон некоторыми настройками Webpack, ответственными за оптимизацию сборок проектов.

Я подготовил шаблон reactjs-boilerplate, позволяющий создавать проекты, готовые к работе в продакшне. Тут используется соответствующая конфигурация сборки, линтинг и хуки, ответственные за проверку проекта перед созданием коммитов. Испытайте этот шаблон. Если у вас появятся какие-то идеи по его совершенствованию, или если вы решите сделать вклад в его разработку — присоединяйтесь к работе над ним.

Итоги


Вот о чём мы говорили в этом материале:

  • Мы разобрали плюсы и минусы create-react-app.
  • Мы реализовали в своём проекте полезную возможность CRA по созданию заготовок React-приложений одной командой. А от недостатков CRA мы избавились.
  • Мы оснастили свой проект минимальными конфигурациями Webpack и Babel, необходимыми для сборки и запуска React-приложений.
  • Мы создали React-компонент HelloWorld.js, предусмотрели возможность сборки проекта и его запуска с использованием сервера разработки.
  • Мы создали исполняемый JS-файл и связали его с соответствующей командой, воспользовавшись разделом bin файла package.json.
  • Мы воспользовались командой npm link с целью создания локальной связи для нашего проекта и получения возможности создавать новые базовые проекты на его основе с помощью единственной команды.

Надеюсь, этот материал поможет вам в деле разработки проектов, основанных на React.

Пользуетесь ли вы create-react-app при создании новых React-проектов?

Источник: https://habr.com/ru/company/ruvds/blog/515008/


Интересные статьи

Интересные статьи

Данное руководство, является "форком" одноименной статьи про CentOS 5.9, и учитывает особенности новой OS. На данный момент в AWS Marketplace нет официального образа Centos8 от centos....
Компьютер — это просто. Или нет? Любой человек, интересующийся сферой информационных технологий, хоть раз задумывался о том, как на самом деле работает компьютер. А и действительно, с помощью ко...
Среди советов по улучшению юзабилити интернет-магазина, которые можно встретить в инете, один из явных лидеров — совет «сообщайте посетителю стоимость доставки как можно раньше».
Как выдумаете, сложно ли написать на Python собственного чатбота, способного поддержать беседу? Оказалось, очень легко, если найти хороший набор данных. Причём это можно сделать даже без нейросет...
Материал, перевод которого мы сегодня публикуем, посвящён созданию динамических PDF-файлов с использованием HTML-кода в качестве шаблона. А именно, речь пойдёт о том, как сформировать простой счё...