Skip to content

Latest commit

 

History

History
491 lines (343 loc) · 25.7 KB

13. Про Вебпак, Бейбель, Еслинт без create-react-app.md

File metadata and controls

491 lines (343 loc) · 25.7 KB

Про Вебпак, Бейбель, Еслинт без create-react-app

Разбираемся в тех технологиях, о которых ходят слухи, что они сложные

Сегодня мы разберёмся с тем, как устроен create-react-app под собой.

Еслинт

Окей, мы поставили преттир и настроили Еслинт на работу с дефолтным конфигом CRA (eslint-config-react-app).

Еслинт это утилита, которая анализирует ваш код по заданным правилам.

Работает он сам по себе легко: вы ставите eslint как зависимость и в файле конфига задаёте настройки, по которым он работает.

Настроек много, но нас интересуют три: правила (rules), плагины (plugins) и расширение через готовый конфиг (extends).

Правила

Основа Еслинта это правила: вы указываете готовое правило (список из дефолтных) и как Еслинту нужно себя вести.

Показываем неиспользуемые значения

Пример: вы пишите код и хотите знать когда у вас остался мусор — вы объявили какую-нибудь константу или функцию, но не используете её. За это отвечает правило no-unused-vars (уточню, что vars тут в контексте слова variables, а не var из Джса).

Вы создаёте .eslintrc.json и там прописываете это правило:

{
  "rules": {
    "no-unused-vars": "error"
  }
}

"error" означает что это грубая ошибка. Бывает ещё "off" и "warn".

Запретим использовать var

В ES6 (он же ES2015) — версия стандарта ECMAScript, по которому работает Джс — добавили let и const.

Как вы помните из второго урока, отличие в том, что const это неизменяемое значение, а у let область видимости ограничена блоком, где она объявлена (но при этом переопределять можно).

Мы не хотим использовать var, поэтому нам поможет правило no-var.

В .eslintrc.json дописываем

{
  "rules": {
    "no-unused-vars": "error",
+   "no-var": "error"
  }
}

Плагины

Но как запретить использовать и let тоже? Для этого есть плагин eslint-plugin-fp, он форсит функциональное программирование, в том числе и иммутабельность.

Плагины подключаются легко: ставите как зависимость, потом в .eslintrc.json прописываете поле plugins с массивом используемых плагинов, а в rules — правило, где префиксом будет название плагина. Щас покажу!

{
+ "plugins": ["fp"],
  "rules": {
    "no-unused-vars": "error",
    "no-var": "error",
+   "fp/no-let": "error"
  }
}

(кстати, в перечислении плагинов можно опустить префикс eslint-plugin- и оставить только fp)

Есть очень много плагинов, например, eslint-plugin-react который отвечает за работу с Реактом или eslint-plugin-import который смотрит за импортами.

Готовые конфиги

Но Женя, неужели это всё руками прописывать надо? Огромные же конфиги получаются, плюс Еслинт и плагины обновляются, правила добавляются, мне что, за этим следить постоянно?

Слава богу нет! У многих плагинов есть рекомендуемая конфигурация, как и у Еслинта. Использование поля extends описано в документации.

Экстендить можно только несколько конфигов, например, можно взять eslint:recommended, plugin:fp/recommended и plugin:react/recommended:

{
  "plugins": ["fp", "react"],
+ "extends": [
+   "eslint:recommended",
+   "plugin:fp/recommended",
+   "plugin:react/recommended"
+ ],
  "rules": {
    "no-unused-vars": "error",
    "no-var": "error",
    "fp/no-let": "error"
  }
}

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

Конфиг от Эйрбнб

Эйрбнб очень много вкладываются в опенсорс и много лет назад написали популярный стайлгайд по Джаваскрипту — советую его прочитать.

Что такое стайлгайд? Это правила. С чем работает еслинт? С правилами. В итоге ребята сделали свой конфиг по этому стайлгайду: eslint-config-airbnb.

Он самый жёсткий из всех: ваш код будет на 99% красным, но зато он будет самым красивым и правильным из всех, что когда-либо показывали на откликах и собеседованиях ❤️

Жизнь без Преттира и Еслинта:

Поле parser и Бейбель

Когда вы поставите конфиг от Эйрбнб, Еслинт может сломаться и начать выдавать unexpected token = и другие ошибки. За это отвечает парсер — Еслинт не может спарсить (проанализировать) ваш код.

По-умолчанию Еслинт использует Espree, но он не обновлялся долгое время и поэтому не поддерживает ES6 и другие нововведения Джаваскрипта (помните про babel-plugin-transform-class-properties?). Но, к счастью, парсер в Еслинте можно сменить через поле parser, вам понадобится babel-eslint.

Как вы понимаете, использовать легко: ставите как зависимость, указываете в поле parser

{
+ "parser": "eslint",
  "plugins": ["fp", "react"],
  "extends": [
    "eslint:recommended",
    "plugin:fp/recommended",
    "plugin:react/recommended"
  ],
  "rules": {
    "no-unused-vars": "error",
    "no-var": "error",
    "fp/no-let": "error"
  }
}

Бейбель

Кстати, о Бейбеле мы ещё не говорили.

Бейбель это компилятор Джаваскрипта в Джаваскрипт.

Если вы проходили курс по вёрстке, то там есть урок про ПостЦСС и cssnext — способ писать будущий ЦСС уже сегодня. Достигается это за счёт того, что новый код превращается в старый с помощью ПостЦСС.

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

Хотите узнать как работают компиляторы? Почитайте The Super Tine Compiler.

Помните, в третьем уроке мы говорили про JSX и что это всего лишь синтаксический сахар и на самом деле Реакт построен на методах React.createElement()? Именно этим и занимается Бейбель и плагин babel-plugin-transform-react-jsx: превращает удобный для вас JSX в понятный браузеру React.createElement(). Попробуйте в REPL.

// input
import React from "react";

const HelloWorld = () => <div>Hello, world</div>;

<HelloWorld />;

// output
("use strict");

var _react = require("react");

var _react2 = _interopRequireDefault(_react);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

var HelloWorld = function HelloWorld() {
  return _react2.default.createElement("div", null, "Hello, world");
};

_react2.default.createElement(HelloWorld, null);

У Бейбеля есть много плагинов, но, как и Еслинт, есть готовые пакеты — они называются пресетами (preset).

Раньше были популярны stage-*-пресеты, но потом от них решили отказаться в пользу babel-preset-env.

Что за стейджи? Стандарт Джаваскрипта (ECMAScript) развивается, им занимается комитет TC39. Работает он так: кто-то описывает новую функциональность в Джаваскрипте, кидает пропозал (proposal), комитет обсуждает на одной из встречи и двигает по стейджам: от 0 (самый сырой черновик) до 4 (вошло в стандарт). Список всех текущих пропозалов можно увидеть в репозитории tc39/proposals.

Например, в стандарте уже есть Array.prototype.includes, async функции, методы Object.entries() и Object.values() и другие.

Раз они есть в стандарте — браузеры должны реализовать, но мы же не можем так долго ждать, нам нужно писать код с этими фичами уже сегодня! Для этого и существуют плагины Бейбеля, например, babel-plugin-array-includes.

На самом деле, этот блок про Бейбель чисто теоретический: в CRA он настолько хорошо настроен, что вам вряд ли придётся с ним столкнуться.

CRA использует собственный пресет babel-preset-react-app, куда уже включены плагины babel-plugin-transform-object-rest-spread и babel-plugin-transform-react-jsx, но если бы вы хотели написать свой конфиг, то ваш .babelrc выглядел бы так:

{
  "plugins": ["transform-object-rest-spread", "transform-react-jsx"]
}

(как и у Еслинта, префикс babel-plugin- можно опустить)

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

Поставьте babel-cli и запустите его как yarn run babel [src] -d [dest], где src это директория с исходниками, а dest это куда нужно скопировать результат. Пример: yarn run babel src -d build.

Всем этим (и другим) занимается Вебпак.

Вебпак

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

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

Давайте попробуем собрать демку в отдельном проекте без CRA? Напишем тот же Hello World на Реакте и соберём всё Вебпаком.

Конечная структура у нас будет такая:

webpack-demo
├── .gitignore       # пропишем сами
├── package.json     # создастся через yarn init
├── webpack.config.js
├── node_modules
│   └── ...
├── src              # создадим руками
│   └── index.js     # наше Реакт-приложение
├── build            # создаётся Вебпаком
│   └── bundle.js
└── public           # создадим руками
    └── index.html

Файлы src/index.js и public/index.html мы создадим руками, а потом — вызовем yarn init чтобы создать package.json.

И не забудьте сделать Гитигнор, иначе туда улетит build и node_modules, а этого не стоит делать — в репозитории должны быть только исходники.

.gitignore

# dependencies
/node_modules

# production
/build

# misc
.DS_Store
.vscode
yarn-error.log

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>React App</title>
</head>

<body>
  <div id="app"></div>
</body>

</html>

src/index.js

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

function App() {
  return <h1>Hello, world</h1>;
}

render(<App />, document.getElementById("app"));

После этого ставим через Ярн webpack-cli и webpack-dev-server: yarn add --dev webpack-cli webpack-dev-server.

Первый будет билдить наш проект, второй — отслеживать изменения когда мы работаем локально.

webpack.config.js

// подключим модуль `path` из ноды,
// чтобы построить абсолютный путь
// до директории build
const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "build")
  }
};

Теперь пробуем запустить как yarn webpack и увидим ошибку:

WARNING in configuration
The 'mode' option has not been set. Set 'mode' option to 'development' or 'production' to enable defaults for this environment.

ERROR in ./src/index.js
Module parse failed: Unexpected token (6:4)
You may need an appropriate loader to handle this file type.
| function App() {
|   return (
|     <h1>Hello, world</h1>
|   )
| }

Ну, во-первых, поставим ещё mode: "development" в webpack.config.js, а, во-вторых, исправим ошибку.


Про настройку Вебпака ходит много смешков, потому что сам он ничего не умеет. Он как Бейбель: расширяется через плагины, только тут они называются лоадерами.

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

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

import logo from "./logo.svg";

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

Но как нам работать с ошибкой выше? Мы же написали нормальный код, джаваскриптовский!

Нам понадобится babel-loader c пресетами babel-preset-env (для современного Джаваскрипта с const, async-await и прочим) и babel-preset-react (для Джсх).

yarn add --dev babel-loader babel-core babel-preset-env babel-preset-react

И, конечно же, нужно отредактировать конфиг Вебпака, чтобы он знал, что на .js файлы нужно натравить именно babel-loader.

const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "build")
  },
  module: {
    rules: [
      {
        test: /\.js$/, // регулярное выражение
        exclude: path.join(__dirname, "node_modules"),
        use: {
          loader: "babel-loader",
          options: {
            presets: ["babel-preset-env", "babel-preset-react"]
          }
        }
      }
    ]
  }
};

Когда я писал этот урок, Бейбел был ещё 6.x версии. В 7.x пакеты вместо babel-x стали @babel/x. Для этого, кстати, используется Scoped Packages в Нпм/Ярн.

Пробуем запустить?

webpack-demo $ yarn webpack
yarn run v1.5.1
$ /private/tmp/webpack-demo/node_modules/.bin/webpack
Hash: 708efebfede93aa32071
Version: webpack 4.1.1
Time: 700ms
Built at: 2018-3-20 10:48:12
    Asset      Size  Chunks             Chunk Names
bundle.js  3.54 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./src/index.js] 430 bytes {main} [built]

ERROR in ./src/index.js
Module not found: Error: Can't resolve 'react' in '/private/tmp/webpack-demo/src'
 @ ./src/index.js 3:13-29

ERROR in ./src/index.js
Module not found: Error: Can't resolve 'react-dom' in '/private/tmp/webpack-demo/src'
 @ ./src/index.js 7:16-36

Ага, опять ошибки, но теперь понятные: нужно просто поставить react и react-dom. Установили, пробуем ещё раз:

webpack-demo $ yarn webpack
yarn run v1.5.1
$ /private/tmp/webpack-demo/node_modules/.bin/webpack
Hash: c1cba3ec70e6f308e028
Version: webpack 4.1.1
Time: 1341ms
Built at: 2018-3-20 10:49:36
    Asset     Size  Chunks                    Chunk Names
bundle.js  634 KiB    main  [emitted]  [big]  main
Entrypoint main [big] = bundle.js
[./src/index.js] 430 bytes {main} [built]
    + 23 hidden modules
✨  Done in 2.35s.

Готово! Проверяем нашу директорию build и видим красивый файл bundle.js

Его же мы и подключаем в public/index.html и потом открываем в браузере.

webpack-dev-server

А что делать, если вы очень много разрабатываете и постоянно вызывать yarn webpack утомляет?

Для этого есть webpack-dev-server: небольшой сервер, который крутится на http://localhost:8080/ и раздаёт файлы оттуда.

webpack-demo $ yarn webpack-dev-server
yarn run v1.5.1
$ /private/tmp/webpack-demo/node_modules/.bin/webpack-dev-server
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wdm」: Hash: 58502b2391c6e006927e
Version: webpack 4.1.1
Time: 1931ms

Нас интересуют две строчки:

ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /

Первая означает, что Вебпак раздаёт файлы по адресу http://localhost:8080/, а вторая — что файлы находятся в корне, этого адреса.

Поэтому нам нужно заменить в public/index.html адрес до Джс-файла:

<script  src="../build/bundle.js"></script>

<script  src="http://localhost:8080/bundle.js"></script>

CRA

Для вас команда фейсбука и CRA сделали одну небольшую команду react-script build (она же прописана в package.json > scripts > build чтобы её можно было вызвать как yarn build). Об этом лучше прочитать в официальной документации CRA.

Итог

Качество кода многое говорит о разработчике. Можно руками соблюдать какой-нибудь стайлгайд, а можно воспользоваться Еслинтом, настроить его и радоваться жизни.

Плюс мы узнали как работает Бейбель — парсер и компилятор Джаваскрипта в… Джаваскрипт; плюс разобрались как развивается ECMAScript.

И третьим пунктом мы прошлись по Вебпаку — мощному, но несложному для новичков бандлеру модулей.

Задание на Еслинт

В вашем проекте воткните самый жёсткий конфиг Еслинта: airbnb-config-eslint. Не забудьте проверить проект на ошибки, вызвав Еслинт.

Учтите, что установка этого конфига не самая тривиальная, поэтому читайте внимательно документацию: там недостаточно написать yarn add --dev eslint-config-airbnb.

Задания на Вебпак

Демо с Вебпаком я выложил на Гитхаб, для вас есть задания:

  • нужен работающий import './styles.css',
  • нужен работающий import logo from './logo.png', где logo будет ссылкой на файл,
  • нужно настроить Вебпак так, чтобы можно было делать import Button from 'ui/Button вместо import Button from '../../../../../../../../../ui/Button.

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

Не забывайте задавать вопросы в чате — там обязательно помогут 💪🏻

Помните: нет глупых вопросов, есть лишь страх их задавать.