Skip to content

Latest commit

 

History

History
412 lines (284 loc) · 23.9 KB

05. react-scripts как спасение наших душ.md

File metadata and controls

412 lines (284 loc) · 23.9 KB

react-scripts как спасение наших душ

Как начать писать код уже сейчас, а не тратить 10 тысяч лет на настройку Вебпака

В прошлом уроке мы попробовали написать совсем крошечное приложение из трёх компонентов, но уже поняли, что с createElement это чертовски сложно — читаемость кода почти нулевая, конечно.

Тем не менее, давайте освежим память прежде чем пойдём в инструменты, которые скроют от нас детали реализаций:

  • Реакт — это чистый Джаваскрипт и работает на функции React.createElement;
  • React это не рендерер, а библиотека для построения дерева элементов и его обновления, за это отвечает ВиртуалДОМ;
  • За рендер в браузер отвечает библиотека ReactDOM, но бывают ещё react-tv, react-native и другие;
  • Реакт это компонентный подход, в котором для передачи данных используют пропы — атрибуты с любыми типами данных (вплоть до функций).

Идем дальше. Учтите, что в этом уроке мы будем настраивать вашу систему для работы и это не должно вас пугать. Погнали!

Нода и пакетные менеджеры Нпм и Ярн

С приветствия я вам рекомендовал пройти урок про инструменты и сейчас вам это должно помочь: мы будем работать с терминалом.

Для начала нам нужно поставить Ноду (Node.js): она выполняет джс-файлы не в браузере, а в виртуальном сервере.

Windows

Скачайте установщик с сайта nodejs.org и через него установите.

Unix

Поставьте из своих репозиториев: apt-get, yum и так далее.

macOS

В Маке нет встроенного пакетного менеджера (а зря!), поэтому сначала нужно поставить Homebrew.

После этого вы можете ставить пакеты и они будут очень аккуратно вписаны в систему.

Поставьте Ноду через команду brew install node.


В Ноде есть пакетный менеджер npm. Он работал плохо и медленно, поэтому ребята из Фейсбука сделали свой пакетный менеджер Yarn: он работает быстрее и предсказуемее, поэтому ставьте его. Установка описана на его сайте, учитесь добывать информацию сами из документаций.

Что такое вообще пакетный менеджер и почему предсказуемость и скорость так важны?

  • Во-первых, во время разработки вы будете использовать много сторонних модулей — ведь кучу вещей кто-то за вас уже когда-то написал, осталось это только подключить и использовать.

  • Во-вторых, говоря про скорость: у каждого пакета могут быть свои зависимости (по 2-3), а в проекте, например, 100 зависимостей. Вместо 100 у вас устанавливается 300: 100 основных пакетов и их зависимостей. Может быть долго!

  • В-третьих, пакеты соблюдают Семвер — семантичное версионирование. Дело в том, что пакеты обновляются, а ломать работающие приложения из-за этого не хочется, поэтому у нас есть версионирование MAJOR.MINOR.PATCH (например, Реакт сейчас — 16.2.0). npm раньше не уважал прописанные в package.json (основной файл проекта) версии и порою всё ломалось. В Ярне такого нет изначально.

У Ярна нет своей базы пакетов, он использует базу нпм.


Окей, Ярн поставили, Нода тоже стоит, всё замечательно. Тут я делаю уточнение: пакеты, которые ставятся через Ярн (ну или нпм, но, опять же, нпм лучше не использовать) могут быть локальными и глобальными. Ставятся они по-разному:

# поставить в систему
yarn global add create-react-app

# поставить локально в проект и записать в `package.json`
yarn add date-fns

# поставить локально и записать в package.devDependencies
yarn add --dev webpack

Пакеты проекта в джс-мире описываются в файле package.json в секциях dependencies и devDependencies.

dependencies

Все зависимости, которые используются в приложении: Реакт, normalize.css из курса по вёрстке, react-day-picker для работы с датами и другие.

devDependencies

Зависимости, которые помогают работать приложению: Еслинт, Преттир, Вебпак и другие.

Чтобы установить все пакеты проекта из package.json, есть команда yarn (альяс к yarn install).

react-scripts и create-react-app

Настройку закончили, переходим к практике.

react-scripts (да, ссылка на нпм, но со всеми нпм-пакетами работаем через Ярн) это пакет, который содержит в себе готовые настройки для Вебпака, Еслинта и Бейбеля — о них мы поговорим в будущем.

Отличие react-scripts от бойлерплейтов в том, что у вас нет кучи конфигов в проекте, они все скрыты от вас в глубинах node_modules/react-scripts/....

Чтобы воспользоваться мощью r-s, достаточно поставить как зависимость и прописать два скрипта в package.json:

...
{
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  }
}

Но вместо этого давайте воспользуемся официальным способом: create-react-app.

CRA это утилита, которая создаёт проект с react-scripts и несколькими компонентами для демонстрации. CRA ставится глобально в систему через Ярн либо можно воспользоваться командой yarn create ...:

# не будет ставить в систему,
# а временно скачает и после удалит
# [dir] это ваша будущая директория, пишется без скобок
yarn create react-app [dir]

Кстати, yarn create ... ищет пакет с названием create-* и вызывает его, поэтому вы можете в будущем воспользоваться yarn create react-native-app для создания проекта через create-react-native-app

Создайте через CRA проект aviasales-demo-frontend

Давайте взглянем на структуру директорий:

/private/tmp $ ls -al aviasales-demo-frontend
total 752
e     Mar  3 22:38 .
e     Mar  3 22:37 ..
e     Mar  3 22:38 .gitignore
e     Mar  3 22:38 README.md
e     Mar  3 22:38 node_modules
e     Mar  3 22:38 package.json
e     Mar  3 22:38 public
e     Mar  3 22:38 src
e     Mar  3 22:38 yarn.lock
  • .gitignore если вы проходили Гитхауту из урока инструментов, вы знаете зачем нужен этот файл. Если нет — идите читать. CRA создал этот файл с директориями и файлами, которые нужно кинуть в игнор в Гите;
  • node_modules — все модули проекта. Туда лезть никогда нельзя;
  • package.json — главный конфиг проекта, от него работают Ярн и нпм;
  • src — директория, где вы пишете код;
  • public — лучше всего ответит документация react-scripts, Using the public Folder;
  • yarn.lock — файл Ярна, где он хранит информацию обо всех установленных пакетах проекта.
  • build — этой директории пока нет, но мы с ней разберёмся на третьем этапе.

Я горячо рекомендую сначала прочитать документацию react-scripts: даже если вы не поймёте, это пассивные знания, которые могут всплыть когда вам они понадобятся, в противном случае вы будете бегать с горящей жопой и не знать что искать когда возникнет проблема. Как вы понимаете, это правило универсально для всех инструментов, которые вы будете использовать. Знайте свой инструмент.

Официальный Юзер Гайд очень полный, я подчеркну несколько моментов:

Можно ли использовать react-scripts в своём проекте не создавая проект через create-react-app? Можно, но не забудьте о том, что react-scripts ориентируется на определенную структуру.

Окей, с этим разобрались. Запускаем проект через команду yarn start и начинаем кодить.

Запускаем браузер по указанному адресу и видим наш сайт:

Открывайте в Вскоде src/App.js и узрите тот самый Джсх, который наконец-то работает!

Настройка Вскода для проекта

Поставьте три плагина: mgmcdermott.vscode-language-babel, dbaeumer.vscode-eslint и esbenp.prettier-vscode.

language-babel

Подсветка синтаксиса: он работает с ES2015+ (стандарт Джаваскрипта), JSX и другими вещами. Вообще, babel это парсер Джаваскрипта и компилятор в понятный браузерам: например, вы можете использовать ES2018 сегодня, а Бейбел переведет их в ES5 (который поддерживают все браузеры). Почему пакет называется language-babel? Потому что babel это в первую очередь парсер и этот пакет его использует.

eslint

Плагин к ESLint. Еслинт — удобная утилита для проверки качества вашего кода. У него есть плагины и готовые конфиги, в create-react-app ставится свой, достаточно мягкий. В будущем мы будем использовать стайлгайд от Эйрбнб — вот он лютый.

prettier

Преттир это форматер кода. Больше никаких споров про отступы, переносы, запятые с точками и прочего.

Настройки

Эти настройки идут в дополнение к вашим, а не полностью их заменяют. Разбирать их не буду, из значений, думаю, понятно что к чему относится, но если нет — не забудьте в чате прояснить.

{
  "editor.tabSize": 2,
  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  "prettier.eslintIntegration": true,
  "javascript.format.enable": false,
  "javascript.validate.enable": false,
  "files.associations": {
    "*.jsx": "javascriptreact",
    "*.js": "javascriptreact"
  },
  "emmet.syntaxProfiles": {
    "javascript": "jsx"
  }
}

Интеграция Еслинта и Вскода в проекте

После того, как создадите приложение через create-react-app, вам нужно будет связать еслинт из него с плагином в Вскоде. Как это сделать? Ответ в официальной документации.


Возвращаемся к проекту

Окей, Вскод настроили, всё заработало, теперь нужно разобраться что за код в src/App.js: импорты, экспорты и классы это нечто новое.

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

Джс: импорты и экспорты

В 2015 году в Джсе появилась система импортов и экспортов. Если вкратце, то один файл может экспортнуть выражение, а второй — импортнуть. Это называется модулями.

В Джс экспорты бывают дефолтные и именованные.

Дефолтные

// sum.js
export default function(left, right) {
  return left + right;
}

// app.js
import sum from "./sum.js";

sum(1, 4); // 5

На самом деле, мы можем называть наш импорт как угодно:

import fuckThis from "./sum.js";

fuckThis(1, 4); // 5

Именованные

Но как быть, если вы хотите экспортнуть несколько констант или функций из одного файла? Для этого есть именованные экспорты.

// math.js
export function sum(left, right) {
  return left + right;
}

export const divide = (left, right) => {
  return left / right;
};

export const mainNumber = 42;

// app.js
import { mainNumber, sum, divide } from "./math.js";

divide(mainNumber, sum(2, 4)); // 7

Джс сначала выполнит sum(2, 4), который вернёт 6, затем это число подставится как второй аргумент divide.

Здесь у нас нет роскоши именовать как угодно, но мы можем импортить as:

// app.js
import { mainNumber as myLovingNumber } from './math.js';

Модули работают через Вебпак

Вебпак — это бандлер модулей. В какой-то момент создатель Вебпака подумал: а зачем нам копировать файлы, подключать всё в index.html по-старинке, если мы можем в джс-файлах импортить вообще всё что угодно?

Для этого используются лоадеры. Вам пока об этом думать не нужно, но вкратце объясню, что лоадеры отвечают за то, что вернёт импорт.

Например, если мы импортим ЦСС-файл, то сработают лоадеры style-loader и css-loader и Вебпак вставит ЦСС-код в <style></style> теги в начале страницы.

Если мы импортим шрифты, картинки или ещё что, сработает file-loader, который при импорте вернёт сгенерированный путь к файлу.

import logo from "./logo.svg";

console.log(logo); // "/static/images/0dcbbaa7013869e351f.png"

Кстати, при импорте директории Вебпак по-умолчанию лезет за index.js или index.jsx файлом, поэтому не нужно писать import Header from './Header/index.js, да .js можно было бы опустить:

import Header from "./Header";

Реакт: классовые компоненты

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

class X extends Component {
  render() {}
}

Это классы, а если конкретнее — классовый компонент Реакта. Мы с этим ещё разберёмся в будущем, сейчас лишь хочу сказать что классовый компонент конкретно здесь идентичен функциональному: метод render() в классе тоже должен вернуть дерево элементов.

Реакт: Джсх

Собственно, Джсх и отвечает за дерево элементов. Про Джсх мы говорили в четвертом уроке, давайте для примера переведём наше приложения про фильмы на Джсх и избавимся от React.createElement.

Старый createElement

// создаём компонент Img, который возвращает <img />
function Img(props) {
  return React.createElement("img", {
    className: props.className,
    src: props.src,
    alt: props.alt
  });
}

// создаём компонент Movie, который возвращает
// |---------| 2001: A Space Odyssey
// |---------|
// |---------|
// |---------|
// |---------|
// |_________|
function Movie(props) {
  return React.createElement(
    "div",
    { className: "movie-page" },
    // чилдреном может быть массив
    [
      // создаём элемент из компонента Img
      React.createElement(Img, {
        className: "movie-img",
        src: props.imgSrc,
        alt: props.title
      }),
      React.createElement("h1", { className: "movie-title" }, props.title)
    ]
  );
}

// создаём
const App = React.createElement(Movie, {
  title: "2001: A Space Odyssey",
  imgSrc: "https://i.imgur.com/vaZoNCA.jpg"
});

ReactDOM.render(App, document.getElementById("app"));

Новый Джсх

// src/App.js
function Img(props) {
  return <img src={props.src} className={props.className} alt={props.alt} />;
}

function Movie(props) {
  return (
    <div className="movie-page">
      <Img className="movie-img" src={props.imgSrc} alt={props.title} />
      <h1 className="movie-title">{props.title}</h1>
    </div>
  );
}

export default Movie;

Аккуратнее стало? Намного!


Почему мы избавились от ReactDOM.render()? Потому что этим занимается src/index.js.

В чём отличие index.js от App.js? App.js это главный файл реакт-приложения, а index.jsфронтэнд-приложения: в вашем проекте может быть много других сторонних библиотек, с которыми нужно будет работать.

Итог

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

Помните, что create-react-app и конкретно react-scripts это ваше спасение и он подходит даже для крупных проектов, поэтому стесняться его не надо.