javascript

Parcel — очень быстрый бандлер, не требующий настройки

  • среда, 13 декабря 2017 г. в 03:13:47
https://habrahabr.ru/post/344486/
  • Разработка веб-сайтов
  • Node.JS
  • JavaScript



Для чего


Parcel — маленький и быстрый бандлер, позиционируется как решение для маленьких проектов. С момента первого релиза (7 дней назад) уже собрал 8725 звездочек на гитхабе. Согласно официальной документации имеет следующие плюсы:


Быстрая сборка


Parcel использует worker process для многопоточной сборки, а так же имеет свой файловый кэш для быстрой пересборки при последующих изменениях.


Собирает все ваши ассеты


Из коробки имеется поддержка ES6, TypeScript, CoffeeScript, HTML, SCSS, Stylus, raw-файлов. Плагины не требуются.


Автоматические преобразования


Весь код автоматически проходит через Babel, PostCSS, PostHTML — подхватываются при необходимости из node_modules.


Разделение кода без лишней конфигурации


Используя динамический import(), Parcel разделяет бандл для возможности быстрой начальной загрузки точки входа в приложение


Горячая перезагрузка


Типичный хот-релоад без конфигурации — сохраняете изменения и они автоматически применяются в браузере.


Дружелюбный вывод ошибок


При ошибке подсвечивается кусок кода, в котором она произошла.


Так же на главной странице приводится бенчмарк:


Bundler Time
browserify 22.98s
webpack 20.71s
parcel 9.98s
parcel — with cache 2.64s

Механика работы


Подход Parcel схож с оным у Webpack (тут сложно придумать что-то новое).


У нас есть сущность — Asset. Ассет — это любой файл. Механика работы такова: реализуется интерфейс, который предоставляет логику для превращения файла в AST, разрешения всех зависимостей, применения нужных трансформаций и генерирования итогового кода. Если вас не устраивает работа какого-то ассета из коробки или вы хотите добавить свой — нет ничего сложного.


Дальше в дело вступает Packager. Упаковщик склеивает ассеты в итоговый бандл. Это происходит после обработки и успешного построения дерева. Упаковщики регистрируются на основе типа файлов. Хотите написать свой упаковщик? Вам сюда.


Так же мы можем писать свои плагины, которые Parcel будет подхватывать из package.json. Для этого у названия пакета плагина должен быть префикс parcel-plugin-. Но это уже совсем частный случай, который скорее всего уже ведет к тому, что надо переключаться на webpack или другой удобный инструмент.


На практике


Ставим пакет, инициализируем приложение через любой пакетный менеджер:


$ yarn global add parcel-bundler
$ mkdir parcel-test && cd parcel-test
$ yarn init -y

Для примера напишем hello world на Preact. Создадим следующую структуру:


parcel-test
├── package.json
├── src
│   ├── app.jsx
│   ├── components
│   │   └── clock
│   │       └── Clock.jsx
│   └── index.html
└── yarn.lock

3 directories, 5 files

А так же установим необходимые пакеты:


$ yarn add preact babel-plugin-transform-react-jsx postcss-modules autoprefixer

Для того, чтобы сконфигурировать Babel создадим .babelrc со следующим содержанием:


{
  "plugins": [
    ["transform-react-jsx", { "pragma":"h" }]
  ]
}

Для PostCSS:


{
  "modules": true,
  "plugins": {
    "autoprefixer": {}
  }
}

Для autoprefixer:


> 1%
last 2 versions

Листинги компонентов

index.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Parcel demo</title>
</head>
<body>
    <script src="./App.jsx"></script>
</body>
</html>

App.jsx


import { h, render } from 'preact';

import { Clock } from './components/clock/Clock';

render((
    <div>
        <h1>It works!</h1>
        <Clock />
    </div>
), document.body);

Clock.jsx


import { h, Component } from 'preact';
import styles from './Clock.css';

export class Clock extends Component {
    constructor() {
        super();

        this.state = {
            time: Date.now()
        };
    }

    componentDidMount() {
        this.timer = setInterval(() => this.setState({ time: Date.now() }), 1000);
    }

    componentWillUnmount() {
        cleanInterval(this.timer);
    }

    render(props, state) {
        let time = new Date(state.time).toLocaleTimeString();
        return <span className={styles.clock}>{ time }</span>
    }
}

Clock.css


.clock {
    color: green;
}

Результат


И это все. Как можно заметить, мы не потратили ни минуты на написание конфигурационных файлов, за исключением .babelrc и .postcssrc


Подводя некий итог


Перед нами эдакий "Webpack на минималках", предоставляющий возможность быстрого развертывания рабочего окружения для небольшого проекта. Стек технологий по сути ограничен лишь стандартным набором ассетов, но в любой момент его можно расширить и своими собственными. С учетом полной поддержки Babel мы легко можем использовать практически любой другой фреймворк или библиотеку (разве что с Angular будут сложности, ведь писать с его помощью на ES6 и без родного инструментария — задача на любителя), а поддержка PostCSS из коробки является еще одним приятным дополнением.


Из неудобств я пока что могу отметить только одно — при работе с TypeScript бандлер не учитывает пользовательские пути и базовый каталог (секции baseUrl и paths), указанные в файле tsconfig, и, соответственно, не может нормально разрешать пути импортируемых модулей. На гитхабе идет обсуждение решения этой проблемы.