Стоит ли изучать вебпак?
Сегодня инструменты CLI, такие как create-react-app или Vue cli, абстрагируются от большей части конфигурации и предоставляют разумные значения по умолчанию .
Даже в этом случае понимание того, как все работает внутри, полезно, потому что рано или поздно вам нужно будет внести некоторые изменения в настройки по умолчанию.
В этом руководстве мы увидим, на что способен webpack и как его настроить в соответствии с вашими потребностями.
Отказ от ответственности
Мои уроки бесплатны, без каких-либо условий. Это означает, что я не обязан постоянно обновлять их до последних выпусков пакетов. Имейте также в виду, что интерфейсные инструменты меняются так быстро, что я не могу обновлять каждую отдельную запись в блоге так же быстро, как $jsTool вносит критические изменения . Но я стараюсь изо всех сил. Если что-то не работает для вас, напишите мне вежливое электронное письмо, и я постараюсь исправить учебник, если у меня будет время. Наслаждаться!
Терминология. Что такое вебпак?
Как разработчик JavaScript вы должны быть знакомы с термином модуль. Возможно, вы слышали о модулях AMD, UMD, Common JS, модулях ES.
webpack является сборщиком модулей и имеет более широкое определение того, что такое модуль , в частности, для webpack модули:
- Общие JS-модули
- модули AMD
- Импорт CSS
- URL изображений
- Модули ЕС
То есть webpack может принимать зависимости из любого из этих источников.
Конечная цель webpackа — объединить все эти различные источники и типы модулей таким образом, чтобы можно было импортировать все в ваш код JavaScript и, наконец, получить готовый к отправке результат.
Точка входа
Точка входа для webpackа — это отправная точка , из которой собираются все зависимости внешнего проекта. На практике это простой файл JavaScript.
Эти зависимости образуют граф зависимостей .
Точка входа по умолчанию для webpackа (начиная с версии 4) — src/index.js
, и ее можно настроить. webpack может иметь несколько точек входа.
Выход
На выходе в процессе сборки собираются результирующие файлы JavaScript и статические файлы.
Выходная папка по умолчанию для webpackа (начиная с версии 4) dist/
также настраивается.
Результирующие файлы JavaScript являются частью так называемого пакета .
Загрузчики
Загрузчики — это сторонние расширения, которые помогают webpackу работать с различными расширениями файлов . Например, есть загрузчики для CSS, для изображений или для файлов txt.
Целью загрузчика является преобразование файлов (кроме JavaScript) в модули. Как только файл становится модулем, webpack может использовать его как зависимость в вашем проекте.
Плагины
Плагины — это сторонние расширения, которые могут изменить работу webpackа . Например, есть плагины для извлечения HTML, CSS или для настройки переменных среды.
Режим
Webpack имеет два режима работы: разработка и производство. Основное различие между ними заключается в том, что рабочий режим автоматически применяет минимизацию и другие оптимизации к вашему коду JavaScript.
Разделение кода
Разделение кода или отложенная загрузка — это метод оптимизации, позволяющий избежать больших пакетов.
При разделении кода разработчики могут решить загружать целые блоки JavaScript только в ответ на какое-либо взаимодействие с пользователем, например клики или изменения маршрута (или другие условия).
Разделенный фрагмент кода становится фрагментом .
Начало работы с вебпаком
Чтобы начать работу с webpackом, создайте новую папку и перейдите в нее, чтобы инициализировать проект NPM:
mkdir webpack-tutorial && cd $_
npm init -y
Внутри установите webpack, webpack-cli и webpack-dev-server :
npm i webpack webpack-cli webpack-dev-server --save-dev
Чтобы легко запустить webpack из скрипта NPM, откройте package.json
и настройте скрипт «dev»:
"scripts": {
"dev": "webpack --mode development"
},
Этим скриптом мы указываем вебпаку работать в режиме разработки , удобном для работы локально.
Первые шаги с вебпаком
Чтобы запустить webpack в режиме разработки:
npm run dev
Вы должны увидеть следующую ошибку.
ERROR in Entry module not found: Error: Can't resolve './src'
Здесь webpack ищет точку входа по умолчанию, src/index.js
. Создайте папку и внутри той же папки создайте простой файл JavaScript:
mkdir src
echo 'console.log("Hello webpack!")' > src/index.js
Теперь запустите снова npm run dev
, и вы больше не увидите ошибок. Результатом этого запуска является новая папка с именем dist/
, в которой находится файл JavaScript с именем main.js
:
dist
└── main.js
Это ваш первый пакет webpack , также называемый output.
Настройка webpackа
Для более простых задач webpack может работать без настройки, но вы довольно скоро достигнете предела. Для настройки вебпака через файл создайте webpack.config.js
в папке проекта:
touch webpack.config.js
Webpack написан на JavaScript и работает поверх безголовой среды JavaScript, такой как Node.js. В этом файле вам понадобится как минимум module.exports
, который является общим экспортом JS для Node.js:
module.exports = {
//
};
Мы webpack.config.js
можем изменить поведение webpackа, добавив или изменив:
- точка входа
- выход
- грузчики
- плагины
- разделение кода
Например, чтобы изменить путь к точке входа, мы можем сделать:
const path = require("path");
module.exports = {
entry: { index: path.resolve(__dirname, "source", "index.js") }
};
Теперь webpack будет искать source/index.js
первый загружаемый файл. Чтобы вместо этого изменить вывод нашего пакета, мы можем сделать:
const path = require("path");
module.exports = {
output: {
path: path.resolve(__dirname, "build")
}
};
С этой конфигурацией webpack поместит пакет build
вместо файла dist
. (Для простоты мы будем придерживаться значения по умолчанию в этом руководстве).
Работа с HTML
Веб-приложение без HTML-страницы практически бесполезно. Для работы с HTML в webpack нам нужно установить плагин html-webpack-plugin :
npm i html-webpack-plugin --save-dev
После того, как плагин установлен, мы можем настроить его:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html")
})
]
};
Здесь мы говорим webpackу, загрузите HTML-шаблон из файла src/index.html
.
Конечная цель html-webpack-plugin двояка :
- он загружает наши файлы HTML
- он внедряет пакет(ы) в тот же файл
Прежде чем двигаться дальше, создайте простой HTML-файл в формате src/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack tutorial</title>
</head>
<body>
</body>
</html>
Через секунду мы запустим это «приложение» на сервере разработки webpack.
Сервер разработки webpack
В первом разделе этого руководства мы установили webpack-dev-server . Если вы забыли добавить его, установите пакет сейчас:
npm i webpack-dev-server --save-dev
webpack-dev-server — удобный пакет для разработки. После настройки мы можем запустить локальный сервер для обслуживания наших файлов.
Чтобы настроить webpack-dev-server , откройте package.json
и добавьте «стартовый» скрипт:
"scripts": {
"dev": "webpack --mode development",
"start": "webpack serve --open 'Firefox'",
},
С помощью этого скрипта мы можем легко запустить сервер. Теперь запустите:
npm start
Ваш браузер по умолчанию должен открыться. В консоли браузера вы также должны увидеть тег скрипта с внедренным нашим основным пакетом JavaScript:
Работа с загрузчиками webpack
Загрузчики — это сторонние расширения, которые помогают webpackу работать с различными расширениями файлов . Например, есть загрузчики для CSS, для изображений или для файлов txt.
Анатомия загрузчика webpackов с точки зрения конфигурации выглядит следующим образом:
module.exports = {
module: {
rules: [
{
test: /\.filename$/,
use: ["loader-b", "loader-a"]
}
]
},
//
};
Соответствующая конфигурация начинается с module
ключа. Внутри этого ключа мы настраиваем каждую группу загрузчиков или отдельный загрузчик внутри rules
.
Для каждого файла, который мы хотим рассматривать как модуль , мы настраиваем объект с test
ключом и с помощью use
:
{
test: /\.filename$/,
use: ["loader-b", "loader-a"]
}
test
сообщает webpackу: «Эй, считай это имя файла модулем». use
вместо этого определяет, какие загрузчики применяются к файлу.
Работа с CSS
Для работы с CSS в webpack нам нужно установить как минимум два загрузчика .
Загрузчики здесь необходимы для того, чтобы помочь вебпаку понять, как обращаться с .css
файлами.
Чтобы протестировать CSS в webpack, создайте простую таблицу стилей в src/style.css
:
h1 {
color: orange;
}
Кроме того, добавьте элемент HTML в наш шаблон HTML в src/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack tutorial</title>
</head>
<body>
<h1>Hello webpack!</h1>
</body>
</html>
Наконец, загрузите CSS в src/index.js
:
import "./style.css";
console.log("Hello webpack!");
Перед тестированием страницы нам необходимо установить загрузчики:
- css-loader для загрузки файлов CSS с помощью
import
- загрузчик стилей для загрузки таблицы стилей в DOM
Устанавливаем загрузчики:
npm i css-loader style-loader --save-dev
Затем настройте их в webpack.config.js
:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html")
})
]
};
Соответствующая конфигурация начинается с module
ключа:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
//
};
Теперь, если вы запустите npm start
, вы должны увидеть таблицу стилей, загруженную в заголовок HTML:
После установки загрузчиков CSS вы можете извлекать файлы CSS с помощью MiniCssExtractPlugin .
Порядок загрузчиков webpack имеет значение!
В webpack большое значение имеет порядок появления загрузчиков в конфигурации . Следующая конфигурация недействительна:
//
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["css-loader", "style-loader"]
}
]
},
//
};
Здесь «style-loader» стоит перед «css-loader». Но загрузчик стилей предназначен для внедрения стиля на страницу, а не для загрузки фактического файла CSS.
Вместо этого допустима следующая конфигурация:
//
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
//
};
загрузчики webpackов загружаются справа налево (или думайте сверху вниз).
Работа с SASS
Для работы с SASS в webpack нам нужно установить как минимум соответствующие загрузчики.
Загрузчики здесь необходимы для того, чтобы помочь вебпаку понять, как обращаться с .scss
файлами.
Чтобы протестировать SASS в webpack, создайте простую таблицу стилей в src/style.scss
:
@import url("https://fonts.googleapis.com/css?family=Karla:weight@400;700&display=swap");
$font: "Karla", sans-serif;
$primary-color: #3e6f9e;
body {
font-family: $font;
color: $primary-color;
}
Кроме того, добавьте еще несколько элементов HTML в наш HTML-шаблон в src/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack tutorial</title>
</head>
<body>
<h1>Hello webpack!</h1>
<p>Hello sass!</p>
</body>
</html>
Наконец, загрузите файл SASS в src/index.js
:
import "./style.scss";
console.log("Hello webpack!");
Перед тестированием страницы нам нужно установить загрузчики (и пакет sass для Node.js):
- sass-loader для загрузки файлов SASS с помощью
import
- css-loader для загрузки файлов CSS в виде модулей
- загрузчик стилей для загрузки таблицы стилей в DOM
Устанавливаем загрузчики:
npm i css-loader style-loader sass-loader sass --save-dev
Затем настройте их в webpack.config.js
:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html")
})
]
};
Опять же, соответствующая конфигурация начинается с module
ключа:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
},
//
};
Обратите внимание на порядок, в котором появляются загрузчики : сначала sass-loader, затем css-loader, наконец, style-loader.
Теперь, если вы запустите npm start
, вы должны увидеть таблицу стилей, загруженную в заголовок HTML:
После установки загрузчиков SASS и CSS вы можете извлекать файлы CSS с помощью MiniCssExtractPlugin .
Работа с современным JavaScript
webpack сам по себе не знает, как преобразовывать код JavaScript. Эта задача передана стороннему загрузчику, в частности, babel-loader , с помощью babel .
babel — это компилятор и «транспилятор» JavaScript. Используя в качестве входных данных современный синтаксис JavaScript, Babel может преобразовать его в совместимый код , который может работать (почти) в любом браузере.
Прежде чем двигаться дальше, нам нужно установить несколько пакетов:
- ядро Babel , настоящий движок
- Предустановленная среда babel для компиляции современного Javascript до ES5
- загрузчик babel для webpack
Подтянем зависимости:
npm i @babel/core babel-loader @babel/preset-env --save-dev
Затем настройте babel, создав новый файл babel.config.json
. Здесь мы настраиваем babel для использования preset-env :
{
"presets": [
"@babel/preset-env"
]
}
Наконец, настройте webpack для использования загрузчика для преобразования файлов JavaScript (я оставил загрузчик SASS слишком для большего контекста):
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "src", "index.html")
})
]
};
Чтобы протестировать преобразование, напишите современный синтаксис src/index.js
:
import "./style.scss";
console.log("Hello webpack!");
const fancyFunc = () => {
return [1, 2];
};
const [a, b] = fancyFunc();
Теперь запустите npm run dev
, чтобы увидеть преобразованный код в формате dist
. Откройте dist/main.js
и найдите «fancyFunc»:
\n\nvar fancyFunc = function fancyFunc() {\n return [1, 2];\n};\n\nvar _fancyFunc = fancyFunc(),\n _fancyFunc2 = _slicedToArray(_fancyFunc, 2),\n a = _fancyFunc2[0],\n b = _fancyFunc2[1];\n\n//# sourceURL=webpack:///./src/index.js?"
Без Babel код не будет транспилирован:
\n\nconsole.log(\"Hello webpack!\");\n\nconst fancyFunc = () => {\n return [1, 2];\n};\n\nconst [a, b] = fancyFunc();\n\n\n//# sourceURL=webpack:///./src/index.js?");
Примечание : webpack отлично работает даже без babel. Процесс транспиляции необходим только для доставки ES5.
Как настроить React, webpack 5 и Babel с нуля
Чтобы использовать компоненты React с webpackом, помимо загрузчика babel, вы также должны установить предустановку babel для React:
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev
После этого настройте babel для использования предустановки React в babel.config.json
:
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
На этом этапе вы можете установить React с помощью:
npm i react react-dom
Наконец, вы можете написать свои компоненты в проекте. Чтобы протестировать ситуацию, вы можете создать компонент в src/index.js
:
import React, { useState } from "react";
import { render } from "react-dom";
function App() {
const [state, setState] = useState("CLICK ME");
return <button onClick={() => setState("CLICKED")}>{state}</button>;
}
render(<App />, document.getElementById("root"));
Теперь при запуске сервера разработки webpack npm start
вы должны увидеть компонент в браузере. (Не забудьте добавить <div>
на страницу с соответствующим идентификатором).
Работа с модулями JavaScript в webpack
webpack рассматривает целый ряд файлов как модули. Но не будем забывать его основное предназначение: загрузка ES-модулей .
До 2015 года в JavaScript не было стандартного механизма повторного использования кода. Было много попыток стандартизировать этот аспект, что в течение многих лет приводило к беспорядочной фрагментации.
Возможно, вы слышали о модулях AMD, UMD или Common JS. Явного победителя не было. Наконец, в ECMAScript 2015 в языке появились модули ES. Теперь у нас есть «официальная» модульная система.
Webpack доставляет удовольствие, работая с модулями ES и модульным кодом.
Чтобы опробовать модули ES в webpack, давайте создадим модуль в новом файле src/common/usersAPI.js
со следующим кодом:
const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";
export function getUsers() {
return fetch(ENDPOINT)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => json);
}
Теперь src/index.js
вы можете загрузить модуль и использовать функцию:
import { getUsers } from "./common/usersAPI";
import "./style.scss";
console.log("Hello webpack!");
getUsers().then(json => console.log(json));
Для освежения знаний о модулях ES: все, что мне нужно знать о модулях ECMAScript .
Режим производства
Как было сказано ранее, у webpack есть два режима работы: разработка и производство . Пока мы работали только в режиме разработки.
В режиме разработки webpack берет весь код JavaScript, который мы пишем, почти в первозданном виде, и загружает его в браузер.
Минификация не применяется. Это ускоряет перезагрузку приложения в процессе разработки.
Вместо этого в рабочем режиме webpack применяет ряд оптимизаций:
- минификация с помощью TerserWebpackPlugin для уменьшения размера пакета
- подъем области с помощью ModuleConcatenationPlugin
Он также настроен process.env.NODE_ENV
на «производство». Эта переменная среды полезна для условных действий в производстве или разработке.
Чтобы настроить webpack в производственном режиме, откройте package.json
и добавьте скрипт «сборки»:
"scripts": {
"dev": "webpack --mode development",
"start": "webpack serve --open 'Firefox'",
"build": "webpack --mode production"
},
Теперь при запуске npm run build
webpack будет создаваться уменьшенный пакет.
Разделение кода с помощью webpackа
Разделение кода относится к методу оптимизации, направленному на:
- избегайте больших пакетов
- избежать дублирования зависимостей
Существует ограничение, которое сообщество webpackов считает максимальным размером исходного пакета приложения: 200 КБ . Чтобы понять, почему первостепенное значение имеет малый размер пакетов, поищите в Google «Стоимость JavaScript».
Есть три основных способа активировать разделение кода в webpack:
- с несколькими точками входа
- с
optimization.splitChunks
- с динамическим импортом
Первый метод, основанный на нескольких точках входа, хорошо работает для небольших проектов, но в долгосрочной перспективе он не масштабируется. Здесь мы сосредоточимся только на optimization.splitChunks
динамическом импорте.
Разделение кода с оптимизацией.splitChunks
Рассмотрим приложение JavaScript, использующее Moment.js , популярную библиотеку JS для времени и дат. Есть лучшие альтернативы этому, но на мгновение (без каламбура) давайте докажем мою точку зрения.
Установите библиотеку в папку вашего проекта:
npm i moment
Теперь сотрите содержимое src/index.js
и импортируйте туда библиотеку:
import moment from "moment";
Запустите сборку npm run build
и посмотрите на результат:
main.js 350 KiB 0 [emitted] [big] main
Вся библиотека собрана в основной точке входа нашего приложения . Фигово. С помощью optimization.splitChunks
мы можем вынести moment.js из основного пакета.
Чтобы настроить разделение кода, откройте webpack.config.js
и добавьте optimization
ключ в свою конфигурацию, настроенную следующим образом:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
module: {
// omitted for brevity
},
optimization: {
splitChunks: { chunks: "all" }
},
// omitted for brevity
};
Запустите сборку npm run build
и посмотрите на результат:
main.js 5.05 KiB 0 [emitted] main
vendors~main.js 346 KiB 1 [emitted] [big] vendors~main
Теперь у нас есть vendors~main.js с moment.js, а основная точка входа имеет более разумный размер.
Примечание : даже с разделением кода moment.js остается гигантской библиотекой. Есть лучшие альтернативы, такие как luxon или date-fns.
Разделение кода с динамическим импортом
Более мощный метод разделения кода использует динамический импорт для условной загрузки кода. Webpack предлагал динамический импорт задолго до того, как эта функция появилась в ECMAScript 2020.
Этот подход широко используется в современных библиотеках внешнего интерфейса, таких как Vue и React (у React есть свой путь, но концепция та же).
Разделение кода может быть использовано:
- на уровне модуля
- на уровне маршрута
Например, вы можете условно загрузить какой-либо модуль JavaScript в ответ на взаимодействие с пользователем, такое как щелчок или движение мыши. Или вы можете загрузить соответствующие части своего кода в ответ на изменения маршрута .
Чтобы приступить к динамическому импорту, удалите содержимое src/index.html
и поместите его в следующий HTML-код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dynamic imports</title>
</head>
<body>
<button id="btn">Load!</button>
</body>
</html>
Убедитесь, что модуль fetch все еще находится в src/common/usersAPI.js
:
const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";
export function getUsers() {
return fetch(ENDPOINT)
.then(response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
})
.then(json => json);
}
Теперь src/index.js
создадим следующую логику:
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
//
});
Ничего не происходит, если бежать npm run start
смотреть и нажимать кнопку в интерфейсе.
Теперь представьте, что мы хотим загрузить список пользователей после того, как кто-то нажмет кнопку. «Наивный» подход может использовать статический импорт для загрузки функции из src/common/usersAPI.js
:
import { getUsers } from "./common/usersAPI";
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
getUsers().then(json => console.log(json));
});
Проблема в том, что модули ES являются статическими, то есть мы не можем изменять импорт во время выполнения.
Вместо этого с помощью динамического импорта мы можем выбирать , когда загружать наш код :
const getUserModule = () => import("./common/usersAPI");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
getUserModule().then(({ getUsers }) => {
getUsers().then(json => console.log(json));
});
});
Здесь мы создаем функцию для динамической загрузки модуля:
const getUserModule = () => import("./common/usersAPI");
Затем в прослушивателе событий мы привязываемся then()
к динамическому импорту:
btn.addEventListener("click", () => {
getUserModule().then(/**/);
});
Это дает возможность извлечь нашу getUsers
функцию с деструктурированием объекта:
btn.addEventListener("click", () => {
getUserModule().then(({ getUsers }) => {
//
});
});
Наконец, мы используем нашу функцию как обычно:
//
btn.addEventListener("click", () => {
getUserModule().then(({ getUsers }) => {
getUsers().then(json => console.log(json));
});
});
Теперь, когда вы загружаете страницу в первый раз npm run start
, вы видите основной пакет, загруженный в консоли:
Теперь "./common/usersAPI" загружается только при нажатии на кнопку :
Ленивый «кусок» — это 0.js
.
Добавляя префикс к пути импорта, /* webpackChunkName: "name_here" */
мы также можем управлять именем чанка:
const getUserModule = () =>
import(/* webpackChunkName: "usersAPI" */ "./common/usersAPI");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
getUserModule().then(({ getUsers }) => {
getUsers().then(json => console.log(json));
});
});
Чанк теперь будет иметь желаемое имя:
Другие темы
Другие интересные вещи, которые стоит пройти:
- предварительная выборка и предварительная загрузка, которые прекрасно работают с динамическим импортом
- кэширование
Подведение итогов - Ресурсы
В этом посте мы рассмотрели основы webpack: разделение кода, настройку, загрузчики, плагины. Конечно, есть намного больше.
После прочтения этого вводного руководства ознакомьтесь с этими замечательными ресурсами:
Спасибо за чтение!