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 initgit init
Создадим файл
.gitignore
следующего содержания:
node_modulesbuild
Это позволит нам не включать в состав репозитория папки, имена которых присутствуют в файле.
Теперь поразмыслим о том, какие основные зависимости нам нужны для того чтобы собрать и запустить 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 nodeconst 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-файлом .gitignorehttps.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-проектов?