Текущее состояние, минимальный css-загрузчик, ICSS2

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

Текущее состояние

Мы предполагаем простой рабочий процесс CSS:

  • который использует cssnext для написания будущего CSS
  • минимизировать CSS в производстве
  • импортирует normalize.css для нормализации CSS

В текущем состоянии это может быть достигнуто с помощью такой конфигурации:

rules: [
  {
    test: /\.css$/,
    use: [
      "style-loader",
      { loader: "css-loader", options: { importLoaders: 1 },
      { loader: "postcss-loader", options: {
        plugins: { "cssnext": {} }
      }}
    ]
  }
]

В нашем примере мы используем такой файл CSS:

body {
  background: url(image.png);
  overflow-wrap: break-word;
}

Сначала это вызывает загрузчик стилей (в фазе шага), который генерирует такой модуль: (упрощенно)

var css = require("-!css-loader??ref-0-1!postcss-loader??ref-0-2!file.css");
require("./addStyle")(css);

Выражение require в первой строке создает новый модуль, который сначала загружает файл css:

body {
  background: url(image.png);
  overflow-wrap: break-word;
}

Затем файл обрабатывается загрузчиком postcss, что дает следующий результат:

body {
  background: url(image.png);
  word-wrap: break-word;
}

Этот результат передается в css-loader, который генерирует такой модуль: (упрощенно)

module.exports = [
  [
    module.id,
    "body {\n  background: url(\"" +
      require("./image.png") + 
    "\");\n  word-wrap: break-word;\n}"
  ]
];

Зачем включать module.id? Загрузчик стилей дедуплицирует стили в соответствии с этим идентификатором.

Этот рабочий процесс включает CSS в пакет JS, но технически может быть лучше использовать отдельные файлы CSS. Это вызывает дополнительный запрос (2 файла на кусок), но CSS может обрабатываться браузером параллельно и кэшироваться отдельно. Для SSR важно, чтобы CSS можно было загрузить до JS.

Текущий рабочий процесс для отдельных файлов CSS включает плагин extract-text-webpack-plugin, который представляет собой большой взлом, который выполняет результат работы css-loader и создает из него файл CSS. Для его выполнения он создает дочерний пакет, который содержит только текущий модуль, нацеленный на node.js, и оценивает его. Вы можете догадаться: это не самое эффективное решение.

Большой план

В долгосрочной перспективе мы хотим сделать возможным добавление первоклассной поддержки модулей для CSS в webpack. Это будет работать следующим образом:

  • Мы добавляем новый тип модуля в webpack: Таблица стилей (рядом с Javascript)
  • Мы настраиваем шаблоны блоков для записи двух файлов. Один для javascript и один из таблиц стилей (в файле .css).
  • Мы настраиваем логику загрузки фрагментов, чтобы разрешить загрузку таблиц стилей. Нам нужно дождаться применения или, по крайней мере, загрузки CSS, прежде чем запускать JS.
  • Когда мы генерируем загрузку фрагмента, мы можем загружать фрагмент js и фрагмент таблицы стилей параллельно (в сочетании с Promise.all).

Скрытая жемчужина: замените таблицу стилей на WebAssembly и .css на .wasm, чтобы получить план для WebAssembly. Они оба очень хорошо подходят друг другу ...

У этого есть несколько преимуществ:

  • Мы можем создавать файлы таблиц стилей для блоков по запросу (это было невозможно с плагином extract-text-webpack-plugin)
  • Использование таблиц стилей намного проще по сравнению с плагином extract-text-webpack
  • Отдельные таблицы стилей будут рабочим процессом по умолчанию
  • Таблицы стилей можно кэшировать независимо для javascript
  • Таблица стилей анализируется только один раз (синтаксическим анализатором css) по сравнению со style-loader (синтаксическим анализатором js как строка + синтаксический анализатор css)

Но также есть несколько ограничений:

  • По запросу загрузка таблиц стилей вызовет два запроса вместо одного по сравнению с загрузчиком стилей.
  • Все содержимое таблицы стилей должно быть доступно для статического обнаружения. Вы не можете создавать содержимое таблицы стилей во время выполнения.
  • publicPath для таблиц стилей или ресурсов, на которые есть ссылка, не могут быть предоставлены во время выполнения. (Это также ограничение плагина extract-text-webpack.)

Первоклассная поддержка CSS заменит style-loader и css-loader:

rules: [
  {
    test: /\.css$/,
    type: "stylesheet", // probably also the default for .css
    use: [
      { loader: "postcss-loader", options: {
        plugins: { "cssnext": {} }
      }}
    ]
  }
]

style-loader и css-loader не будут удалены, поэтому вы все равно можете вернуться к ним, если вам нужны встроенные таблицы стилей.

Шаги

Шаг 1: очистка css-loader

В настоящее время css-loader выполняет множество функций:

  • Ручка @import
  • Обрабатывать ICSS :import и :export
  • Ручка url()
  • Ручка ~module
  • Сведение к минимуму
  • SourceMapping
  • Сглаживание
  • Разветвление вложенных загрузчиков (importLoaders)

Это много и также вызывает проблемы с производительностью в текущем состоянии. На первом этапе мы хотим сократить набор функций и попытаться создать минимально возможное css-loader. Это создает лишь небольшую поверхность API для CSS. Имейте в виду, что тот же API будет использоваться для первоклассных модулей CSS в webpack. Поэтому, когда css-loader выполняет меньше работы, нам нужно делать меньше работы в webpack.

Итак, каков минимальный набор функций для css-loader? Модуль CSS очень похож на модули JS, у него есть код, зависимости и экспорт. Для JS у нас есть модули CommonJS, AMD и ES. Для CSS нет официального стандарта, но для инициативы CSS Modules Initiative Глен Мэддерн разработал его: Interoperable CSS. На момент написания ESM еще не существовало, поэтому его нужно было адаптировать к ESM, но это было лишь незначительное изменение.

При поддержке ICSS минимальный набор функций для загрузчика css будет ICSS + @import. Я обсуждал это с командой разработчиков webpack contrib, и они все время спрашивали: почему мы удаляем url(), но оставляем @import? «По техническим причинам» - вероятно, плохой ответ при разработке API. Итак, мы все равно меняем ICSS (ESM), так почему бы не добавить недостающие части, чтобы заменить @import на ICSS.

Итак, ICSS2 - это ICSS плюс следующие изменения: ESM, медиа-запросы для :import и правила CSS могут быть импортированы с :import.

ICSS2

Вот краткое объяснение Interoperable CSS v2:

:import(<request>) {
  import: <exportName> <media queries>;
  <alias>: <exportName>;
  <alias>: <exportName>;
}

Правило :import позволяет объявить зависимость файла. <request> указывает на импортированный модуль. Запрос разрешается в соответствии с обычными правилами разрешения (./relative ../relative module module/path). Специальный ключ import позволяет импортировать правила CSS из экспорта импортированного модуля. При желании это можно условно импортировать с помощью медиа-запросов (@import также разрешает медиа-запросы, мы не хотим потерять это поведение). Любой другой ключ рассматривается как псевдоним. Каждое вхождение (как идентификатор) этого псевдонима заменяется значением экспорта импортированного модуля.

:export {
  <exportName>: <any value>;
  <exportName>: <any value>;
}

:export довольно просто. Ключ - это экспортируемое имя (должно быть допустимым идентификатором JS, ESM). Строка, переданная как значение, экспортируется. Интервал незначителен, а расстояние между значениями сокращается до одного пробела на пробел, без интерлиньяжа и обучающих пробелов.

Содержимое CSS экспортируется как default экспорт. Формат зависит от реализации, но реализация должна гарантировать, что файлы, импортированные несколько раз, появляются в результате только один раз и что порядок импортированного CSS сохраняется (если возможно).

css-loader с примером ICSS2

@import "mobile.css" (max-width: 400px);
body {
  background: url(./image.png);
}

С новыми плагинами PostCSS (или плагинами для вашего препроцессора) он будет преобразован в эту часть ICSS2:

:import("mobile.css") {
  import: default (max-width: 400px);
}
:import("./image.png") {
  __url_image_png: default;
}
body {
  background: url(__url_image_png);
}

Так что загрузчик CSS, соотв. для поддержки CSS в webpack достаточно двух ключевых слов: :import и :export. Очень просто. Очень производительный, потому что для его синтаксического анализа не нужен полный AST, это сделает преобразование только для строки.

часто задаваемые вопросы

Значит, мы больше не можем использовать модули CSS с загрузчиком css?

Да и нет. Загрузчик css больше не поддерживает его, но с ICSS2 он по-прежнему поддерживает строительные блоки для модулей CSS. Для модулей CSS будет отдельный загрузчик или плагин postcss, преобразующий синтаксис CSSM в ICSS2.

Мне все еще нужно использовать ~module для ссылки из модулей?

Это поведение больше не будет обрабатываться загрузчиком css. Он будет частью плагинов postcss для @import соотв. url(). Будет возможность выбора между стандартными запросами и модульными запросами.

Поскольку importLoaders будет удален, как я могу указать загрузчики, используемые для импортированных ресурсов?

Css-loader больше не будет переопределять загрузчики на импортированном ресурсе. webpack будет отвечать за определение загрузчиков. Это означает, что module.rules применимо и к импортированным загрузчикам. В настоящее время требуется более сложная конфигурация для применения style-loader только к файлам CSS, импортированным из JS:

rules: [
  {
    test: /\.css$/,
    rules: [
      {
        issuer: { not: /\.css$/ },
        use: "style-loader"
      }
      {
        use: "css-loader"
      }
    ]
  }
]

Но теперь это позволяет вам смешивать разные языки компиляции в CSS. Ага!

Этим первым шагом мы заложили основу для производительного css-загрузчика и минимальной реализации CSS в webpack.

Следите за обновлениями, чтобы узнать больше о следующих шагах: как мы планируем интегрировать CSS в качестве первоклассного гражданина в webpack.

webpack не поддерживается крупной компанией, в отличие от многих других крупных продуктов с открытым исходным кодом. Разработка финансируется за счет пожертвований. Если вы зависите от webpack, подумайте о пожертвовании… (Спросите своего босса!)

Особая благодарность этим спонсорам: (Топ 5)

  • Capital One (Банк) пожертвовал $ 12 000
  • Компания ag-Grid (DataGrid) пожертвовала на общую сумму 7 500 долларов США.
  • AngularClass (Обучение) пожертвовал 2,512 долларов
  • Google Angular (Framework) пожертвовал 2500 долларов
  • OpenCollective (финансирование ОС) пожертвовал на общую сумму 1490 долларов.
  • "Полный список"