Привет, Хабр!
Когда-то были времена, когда весь JavaScript-код приложения складывался в один огромный файл. Это было не только неудобно, но и было множество ошибок из-за глобального пространства имен и сложностей с зависимостями. Тогда появилась необходимость в модульности.
Многие при первой встречи с такими проблемами пытались разделить код на отдельные файлы и подключать через тег <script>
. Но такое решение было очень далеко от идеала. Потом были попытки использовать различные библиотеки и инструменты, такие как RequireJS или Browserify, но каждый из них имел свои недостатки и ограничения.
Все изменилось с приходом ES6, который ввел нативную поддержку модулей и один из этих модулей - Rollup. Сегодня мы его и рассмотрим в статье.
Установим и настроим
Создадим директорию для проекта и переходим в неё:
mkdir my-rollup-project && cd my-rollup-project
Инициализируем с помощью NPM:
npm init -y
Установим Rollup как зависимость:
npm install rollup --save-dev
Создадим файл rollup.config.js
в корневой директории проекта:
export default {
input: 'src/main.js',
output: {
file: 'bundle.js',
format: 'cjs'
}
};
Добавим скрипт сборки в package.json
:
"scripts": {
"build": "rollup -c"
}
Для запуска сборки:
npm run build
Добавление CSS
Для работы с CSS потребуется плагин. Установим rollup-plugin-postcss
:
npm install rollup-plugin-postcss --save-dev
Добавим этот плагин в rollup.config.js
:
import postcss from 'rollup-plugin-postcss';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife',
},
plugins: [
postcss({
extensions: ['.css'],
}),
],
};
Теперь можно создать файл стилей styles.css
в папке src
и импортировать его в index.js
:
/* src/styles.css */
body {
background-color: #f0f0f0;
}
// src/index.js
import './styles.css';
console.log('Привет, Хабр!');
Работа с изображениями
Для включения изображений в бандл используем плагин rollup-plugin-img
:
Обновим rollup.config.js, добавив этот плагин:npm install rollup-plugin-img --save-dev
Обновим rollup.config.js
, добавив этот плагин:
import postcss from 'rollup-plugin-postcss';
import img from 'rollup-plugin-img';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife',
},
plugins: [
postcss({
extensions: ['.css'],
}),
img({
limit: 10000 // размер файла в байтах для инлайнинга изображений
}),
],
};
Теперь можно добавить изображение в папку src
и использовать его в CSS или JavaScript. Например, в styles.css
:
body {
background-image: url('./background.png');
background-size: cover;
}
Или напрямую в index.js
:
import background from './background.png';
document.body.style.backgroundImage = `url('${background}')`;
Также установим некоторые нужные плагины
@rollup/plugin-node-resolve: позволяет Rollup подключать внешние модули из node_modules
:
npm install --save-dev @rollup/plugin-node-resolve
Включим в конфигурационный файл:
import resolve from '@rollup/plugin-node-resolve';
export default {
//...
plugins: [resolve()]
};
Абсолютно аналогично можно установить:
@rollup/plugin-commonjs: преобразует модули CommonJS в формат ES6, который может обрабатывать Rollup.
@rollup/plugin-json: позволяет Rollup импортировать данные из JSON-файлов.
@rollup/plugin-babel: интегрирует Babel для транспиляции кода в старые версии JavaScript.
Для Babel нужно будет создать.babelrc
с настройками Babel:
{
"presets": [
["@babel/preset-env", {
"modules": false
}]
]
}
@rollup/plugin-terser: минифицирует итоговый бандл для продакшена.
Основные функции Rollup
Tree-Shaking
Tree-Shaking – это процесс устранения кода, который фактически не используется в проекте.
Tree-Shaking работает, анализируя кодовые файлы, необходимые для запуска приложения, и включая только код, который действительно используется приложением. Этот процесс создаёт структуру данных, представляющую намерения вашего кода, состоящую из узлов, которые представляют код (функции, утверждения и т.д.). Эти узлы формируют структуру, похожую на дерево, и после её создания можно определить, какой код действительно используется приложением .
Tree-Shaking в JS работает благодаря использованию ES6 модулей, потому что они статичны в своей структуре. Каждый файл, проанализированный в процессе Tree-Shaking, будет иметь различные виды операторов экспорта как узел верхнего уровня создаваемой структуры данных. В файлах, которые импортируют эти экспортированные члены, использование импорта может быть отслежено.
Рассмотрим пример использования Tree-Shaking с помощью Rollup, демонстрирующий как неиспользуемый код может быть исключен из итогового бандла.
Предположим, есть два файла модулей: math.js
и main.js
. В math.js
определены несколько функций, но main.js
использует только одну из них.
math.js:
export const sum = (a, b) => a + b;
export const multiply = (a, b) => a * b;
export const subtract = (a, b) => a - b;
main.js:
import { sum } from './math.js';
console.log(sum(1, 2)); // Ожидается вывод: 3
Если мы используем Rollup для сборки, он анализирует зависимости и понимает, что только функция sum
используется в main.js
. Функции multiply
и subtract
не используются и, следовательно, могут быть исключены из финального бандла благодаря механизму Tree-Shaking.
Для запуска Tree-Shaking с Rollup, можно юзать следующий конфигурационный файл Rollup rollup.config.js
:
export default {
input: 'main.js', // точка входа вашего приложения
output: {
file: 'bundle.js', // имя итогового файла
format: 'es' // формат модулей в итоговом бандле
}
};
Затем, запускаем Rollup с этой конфигурацией, и он сгенерирует bundle.js
, содержащий только используемый код. В результате размер итогового файла будет меньше, и приложение загрузится быстрее.
Code Splitting
Code-Splitting позволяет разделить код на разные части на основе различных точек входа и динамических импортов, используя механизм импорта формата вывода вместо пользовательского кода загрузчика . Это позволяет динамически загружать части приложения по мере необходимости.
Для демонстрации Code Splitting с помощью Rollup, рассмотрим пример, где у нас есть две точки входа в приложение и динамический импорт модуля.
Структура проекта:
src/
main1.js
(точка входа 1)main2.js
(точка входа 2)shared.js
(модуль, используемый обеими точками входа)dynamic.js
(модуль, импортируемый динамически)
shared.js:
// экспортируем функцию, общую для main1.js и main2.js
export const sharedFunction = () => console.log('Это общая функция');
dynamic.js:
// экспортируем функцию для динамического импорта
export const dynamicFunction = () => console.log('Это динамическая функция');
main1.js:
import { sharedFunction } from './shared.js';
// использование общей функции
sharedFunction();
// динамический импорт модуля
import('./dynamic.js').then((module) => {
module.dynamicFunction();
});
main2.js:
import { sharedFunction } from './shared.js';
// использование общей функции
sharedFunction();
// предположим, здесь есть дополнительный код специфичный для main2.js
Для поддержки Code Splitting в Rollup конфигурации нужно определить обе точки входа и настроить выходные параметры для генерации нескольких чанков:
rollup.config.js:
export default {
input: ['src/main1.js', 'src/main2.js'],
output: {
dir: 'output',
format: 'esm',
chunkFileNames: '[name]-[hash].js'
},
plugins: [
// здесь можно юзать плагины, например @rollup/plugin-node-resolve для разрешения модулей из node_modules
]
};
input
определяется как массив с двумя точками входа. Rollup будет анализировать зависимости, обнаруживать общие модули и динамические импорты, и соответственно разделять код на чанки. В результате, shared.js
будет вынесен в отдельный чанк, так как используется обеими точками входа, в то время как dynamic.js
будет загружен динамически только при необходимости.
Настройка Rollup для работы с TypeScript, React
Для начала работы с TypeScript и React необходимо установить соответствующие пакеты и зависимости:
npm i --save-dev typescript react react-dom @types/react
Для интеграции TypeScript в проект с Rollup юзаем плагин @rollup/plugin-typescript
. Простейший файл конфигурации rollup.config.js
может выглядеть так:
import typescript from '@rollup/plugin-typescript';
export default {
input: 'src/index.tsx', // точка входа приложения
output: {
dir: 'dist', // каталог для сгенерированных файлов
format: 'esm', // формат модуля ES
},
plugins: [typescript()],
};
Эта конфигурация указывает Rollup на обработку файлов TypeScript и сборку их в модули ES6.
Файл tsconfig.json
содержит настройки компилятора TypeScript. Пример базовой конфигурации:
{
"compilerOptions": {
"outDir": "./dist",
"module": "ESNext",
"target": "es5",
"jsx": "react",
"declaration": true,
"declarationDir": "./dist"
},
"include": ["src/**/*"]
}
Здесь будет поддержка JSX, генерацию деклараций типов и компиляцию в ES5 для лучшей совместимости.
Для работы с React компонентами потребуется настроить Babel вместе с Rollup для транспиляции JSX:
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
Также нужно создать .babelrc
или babel.config.json
файл с соответствующими пресетами:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
]
}
Это позволяет Babel корректно обрабатывать файлы .tsx
и .ts
, содержащие JSX.
Больше практических инструментов эксперты из OTUS рассматривают в рамках онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке, а также не забывайте о нашем календаре бесплатных мероприятий.