javascript

Модули ES6: Rollup

  • четверг, 11 апреля 2024 г. в 00:00:09
https://habr.com/ru/companies/otus/articles/804133/

Привет, Хабр!

Когда-то были времена, когда весь 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 рассматривают в рамках онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке, а также не забывайте о нашем календаре бесплатных мероприятий.