Благость под названием create-react-app [Pt.2 – Automatic React Installation (and how to break it)]

Что же, в прошлый раз мы исследовали прелестное искусство мазохизма и настраивали своеобразный синоним среды разработки самостоятельно – с вебпаком, бейблом и ручной настройкой всего и вся. Но еще в самом начале прошлого рассказа я упомянул, о куда более простой альтернативе:

create-react-app

Что же это? По факту cli – command line interface для создания базового шаблона стандартного приложения на реакте.

В начале своего пути реакт не имел подобных инструментов (да и вообще разрабатывался “по приколу” и “для себя” фейсбуком). Однако, со временем и появлением других подобных библиотек на базе или доработке идей виртуального DOM-а и наличием у них подобных инструментов упрощения жизни разработчиков, сообщество реакта и фейсбук (вроде) наконец созрели для создания своего инструмента.

Почему не сразу? Все просто – чтобы научиться что-то чинить, нужно или это что-то сломать, или построить свой вариант. Ломать это, конечно, весело, но без хоть какого-либо понимания, процесс обратной починки может затянуться, а то и вовсе по факту не начаться. Если у тебя что-то сломается при использовании шаблона созданного c-r-a – ты хотя бы примерно сможешь ориентироваться в каком из узлов всей цепочки сборки и компиляции это произошло и где искать помощи.

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

Впрочем, завершим затянувшееся лирическое отступление и приступим к делу:

Работа с create-react-app

Есть два пути: старый-добрый-глобальный, если у тебя npm ниже версии 5.2, и новый-модный npx, если выше. Отличаются они в одну строчку и кучку деталей (про которые можно почитать здесь(англ) или здесь(рус)), поэтому посмотрим оба (, а я таки обновлю свою стареющую ноду 8.5.0 версии (хотя это делать оказалось необязательно)).

Классический способ это установка cli в глобальную директорию npm:

npm i -g create-react-app

После чего можно использовать: (если это первая глобальная установка, возможно придется добавить путь в PATH на Win или в /bin (или куда нужно) на *nix системах)

create-react-app app-folder-name

Это создаст проект в “app-folder-name” по текущему пути консоли.

Модный способ это использование вышеназванного npx, который поставляется с 5.2 версий npm:

npm install npm@latest -g

Это обновит npm, на случай, если ты хочешь использовать npx, но npm уже старенький. Напоминаю, что проверить версии можно с помощью npm(node) -v

npx create-react-app app-folder-name

Это опять же создаст проект в “app-folder-name” по текущему пути консоли.

Удобство npx состоит в том, что он позволяет как запускать локально установленные версии инструментов (модулей), так и использовать “одноразовые” инструменты вроде create-react-app без их установки в глобальную директорию. Что в последнем случае очень удобно – не приходится следить за версией названных инструментов.

И.. собственно все, в этот мы справились куда быстрее, не так ли? Мы можем запустить вебпак через yarn start и начать создавать!

Но это скучно.

Поэтому, приготовься покинуть кабину, мы катапультируемся!

(Я знаю, что eject это не совсем катапультироваться, но тем не менее)

create-react-app это очень базовый, стандартизированный и проверенный способ начинать работу с реактом, поэтому в нем нет всех тех прикольных штук, которые тебе возможно захочется использовать. К примеру последний писк моды и ES8 с компиляцией от бейбла, или немногословный и немножко страшный, но такой удобный stylus, или тебе просто, как и мне, интересно поковыряться и узнать как они все это настроили.

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

yarn eject

Ах, сколько новых папочек и файлов

К слову, если ты (как любой адекватный человек, да-да) держишь проект в гит-репо, c-r-a не даст тебе катапультироваться без сохранения последних изменений, что очень мило с их стороны, на случай если тебе не понравится опыт досрочного кхем выпуска.

Что же, разберемся по-подробнее с появившимся разнообразием конфигурационных файлов. В особенности меня сейчас интересует configs/webpack.config.dev.js – он содержит конфигурацию для – yarn start – процесса разработки, а ближайшее время мы будем заниматься только им.
К слову, в нем достаточно много подробных комментариев, так что приглашаю заглянуть в него вместе, не дожидаясь моих пояснений.
Также помимо самого файла конфигурации, может быть полезно заглянуть в configs/paths.js – он содержит распознаватель путей и непосредственно их самих – полезно примерно прикидывать что куда указывает.

module.exports = {
  entry: [
    require.resolve('react-dev-utils/webpackHotDevClient'),
    paths.appIndexJs,
  ],
  output: {
    pathinfo: true,
    filename: 'static/js/bundle.js',
    chunkFilename: 'static/js/[name].chunk.js',
    publicPath: '/',
  },
  resolve: {
    modules: ['node_modules', paths.appNodeModules].concat(
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),
    extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
  },
  module: {
    strictExportPresence: true,
    rules: [
      {
        test: /\.(js|jsx|mjs)$/,
        enforce: 'pre',
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
            },
            loader: require.resolve('eslint-loader'),
          },
        ],
        include: paths.appSrc,
      },
      {
        oneOf: [
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
          {
            test: /\.(js|jsx|mjs)$/,
            include: paths.appSrc,
            loader: require.resolve('babel-loader'),
            options: {
              cacheDirectory: true,
            },
          },
          {
            test: /\.css$/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                },
              },
              {
                loader: require.resolve('postcss-loader'),
                options: {
                  ident: 'postcss',
                  plugins: () => [
                    require('postcss-flexbugs-fixes'),
                    autoprefixer({ ... }),
                  ],
                },
              },
            ],
          },
          {
            exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
            loader: require.resolve('file-loader'),
            options: {
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
        ],
      },
    ],
  },
};

Я, как можно заметить, обрезал комментарии и некоторые слишком уж специфичные элементы оптимизации или работы вебпака, с которыми шансы столкнуться приближается к скромному нулю (плюс плагины, опции загрузчиков и бонусы для react-native).

Как можно сразу заметить, entry и output слегка отличаются от нашего самопала, однако большей частью это лишь косметические изменения:

entry может принимать не единственную строку, как в нашем случае, а массив или даже объект строк (и массивов, вложенность, ура), в данном случае помимо нашего приложения вебпак просто обрабатывает сервер для разработки и набор заполнителей выбранный c-r-a; output же использует несколько доп опций:
pathinfo – включает в конечную сборку данные о путях, модулях и прочем,
publicPath – позволяет указать корень выхода – папку, в которую помещаются все сгенерированные файлы, мы не использовали его, так как наш проект содержал всего один файл
chunkFilename – указывает как называть чанки – куски кода оптимизированные для отдельной загрузки и кеширования.

В свою очередь resolve (за исключением страшной конструкции сбора путей и плагина) остался таким же, немного расширив набор поддерживаемых путей и расширений.

И наконец module. Помимо самих загрузчиков, присутствует один параметр strictExportPresence – он указывает вебпаку расценивать не найденный модуль (импорт) как ошибку и сразу же прекращать дальнейшую сборку, что очень полезно, так как сразу же кричит о проблеме, не дожидаясь перезагрузки пака и порой, если используется роутер или компонент загружается не сразу, попадания на пропущенный импорт.

С загрузчиками, впрочем, все достаточно просто и знакомо (спасибо вебпаку за стандартизацию конфигурации):
Первым идет eslint – линтер для JS. Линтер – это “инструмент статического анализа” – или говоря по-человечьи, подсветка ошибок и несоответствий выбранному набору правил написания кода. Очень полезный инструмент, который, что особенно приятно, как правило доступен в виде расширения для редактора.
При этом загрузчик линтера использует специальный модификатор enforce – он нужен, чтобы линтер всегда обрабатывал исходный код до его преобразования бейблом. Модификатор (параметр) enforce задает категорию порядка применения загрузчика, всего доступно 4 опции (pre, inline, normal, post), о которых можно почитать здесь.

После линтера идет основной набор, объединенный через модификатор oneOf – он позволяет обработать файл только одним (первым подходящим) загрузчиком. В конце находится загрузчик просто предоставляющий доступ к файлу, не затрагивая при этом исходный код и стандартные медиа-форматы – png, jpg, т.д – за счет их расположения выше по списку и соответственно порядку загрузки.

На чем наш расковыряный файл конфигурации заканчивается. Я, опять же, мягко указываю тебе покопаться в конфигурации самостоятельно и, к примеру, добавить поддержку любимого css препроцессора или пресета для бейбла. Процесс достаточно простой, и я буду разбирать его в будущем, когда приложению понадобится стилизация (а делать ее на css или тем более встроенных стилях я отказываюсь), но всегда полезно попробовать самостоятельно (имея бекап), прежде чем идти читать руководство.
Также должен заметить, что учитывая базовость и частое нежелание катапультироваться из c-r-a, создали кучку способов изменения или расширения конфигурации, оставаясь технически с c-r-a. Но я сам больше склоняюсь к eject-у, нежели использованию таких инструментов.

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

nil commento load