style-loader — загрузчик Webpack, предназначенный для
внедрения CSS-кода непосредственно в DOM-документ через тег
<style>. Вместо генерации отдельного CSS-файла стили
встраиваются в JavaScript-бандл и подключаются во время выполнения
приложения.
Основная задача загрузчика — обеспечить динамическое подключение стилей в браузере без необходимости отдельной загрузки CSS-файлов.
Наиболее часто используется:
После обработки CSS-файла Webpack передаёт содержимое в
style-loader. Загрузчик:
<style>.<head> документа.Схема работы:
CSS → css-loader → style-loader → <style> в DOM
Без css-loader загрузчик не сможет интерпретировать CSS
как JavaScript-модуль.
npm install style-loader css-loader --save-dev
или:
yarn add style-loader css-loader -D
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
]
}
]
}
};
Webpack применяет загрузчики справа налево.
Конфигурация:
use: [
'style-loader',
'css-loader'
]
эквивалентна:
style-loader(css-loader(file.css))
css-loader@import;url();style-loader<style>;После настройки стили можно импортировать напрямую в Jav * aScript:
import './styles.css';
Во время выполнения Webpack автоматически внедрит CSS в страницу.
При импорте:
import './app.css';
в DOM появится примерно такой код:
<style>
body {
background: #000;
}
</style>
import './reset.css';
import './layout.css';
import './theme.css';
Webpack создаст несколько блоков стилей либо объединит их в зависимости от конфигурации и режима сборки.
npm install sass sass-loader style-loader css-loader --save-dev
module.exports = {
module: {
rules: [
{
test: /\.scss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]
}
};
npm install less less-loader style-loader css-loader --save-dev
{
test: /\.less$/i,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}
npm install postcss postcss-loader autoprefixer --save-dev
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
Одно из ключевых преимуществ style-loader — корректная
работа с HMR.
При изменении CSS:
module.exports = {
devServer: {
hot: true
}
};
style-loaderMiniCssExtractPluginconst MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
isDev
? 'style-loader'
: MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
!isDev &&
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
].filter(Boolean)
};
injectTypeПозволяет управлять способом внедрения стилей.
{
loader: 'style-loader'
}
Эквивалентно:
{
loader: 'style-loader',
options: {
injectType: 'styleTag'
}
}
styleTagСоздаёт отдельный <style> для каждого модуля.
{
loader: 'style-loader',
options: {
injectType: 'styleTag'
}
}
<style>.singletonStyleTagВсе стили помещаются в один общий <style>.
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag'
}
}
lazyStyleTagСтили подключаются вручную.
{
test: /\.lazy.css$/i,
use: [
{
loader: 'style-loader',
options: {
injectType: 'lazyStyleTag'
}
},
'css-loader'
]
}
import styles from './dialog.lazy.css';
styles.use();
styles.unuse();
import modalStyles from './modal.lazy.css';
function openModal() {
modalStyles.use();
}
Изолированное подключение стилей:
widgetStyles.use();
darkTheme.use();
lightTheme.unuse();
lazySingletonStyleTagКомбинирует:
{
loader: 'style-loader',
options: {
injectType: 'lazySingletonStyleTag'
}
}
insertПозволяет указать место вставки <style>.
<body>{
loader: 'style-loader',
options: {
insert: 'body'
}
}
{
loader: 'style-loader',
options: {
insert: function insertAtTop(element) {
const parent = document.querySelector('head');
const lastInsertedElement =
window._lastElementInsertedByStyleLoader;
if (!lastInsertedElement) {
parent.insertBefore(element, parent.firstChild);
} else if (lastInsertedElement.nextSibling) {
parent.insertBefore(
element,
lastInsertedElement.nextSibling
);
} else {
parent.appendChild(element);
}
window._lastElementInsertedByStyleLoader = element;
}
}
}
style-loader может работать с Web Components.
const shadowRoot = element.attachShadow({
mode: 'open'
});
{
loader: 'style-loader',
options: {
insert: function insertIntoShadowRoot(element) {
shadowRoot.appendChild(element);
}
}
}
attributesПозволяет добавлять атрибуты к тегам <style>.
{
loader: 'style-loader',
options: {
attributes: {
id: 'main-styles',
'data-app': 'frontend'
}
}
}
Результат:
<style id="main-styles" data-app="frontend">
baseИспользуется при работе с DLLPlugin и несколькими runtime-бандлами.
{
loader: 'style-loader',
options: {
base: 1000
}
}
Позволяет избегать конфликтов идентификаторов модулей.
module.exports = {
devtool: 'source-map',
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
}
]
}
};
{
test: /\.module\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
.button {
background: red;
}
import styles from './button.module.css';
button.className = styles.button;
css-loader автоматически преобразует:
.button
в:
.button_a1b2c3
Это предотвращает конфликты имён.
{
loader: 'css-loader',
options: {
modules: {
localIdentName:
'[name]__[local]__[hash:base64:5]'
}
}
}
declare module '*.css';
declare module '*.module.css' {
const classes: {
[key: string]: string;
};
export default classes;
}
style-loader имеет особенности, влияющие на
производительность.
CSS становится частью Jav * aScript:
bundle.js
├─ JS
└─ CSS
Стили появляются только после:
<style>.Из-за этого возможен:
При большом количестве CSS:
<style>.style-loader подходит лучше всегоНаиболее распространённый сценарий.
Преимущества:
Особенно при активной разработке интерфейса.
Где производительность первого рендера менее критична.
Позволяет быстро запускать сборку без сложной оптимизации.
style-loader использовать нежелательноОсобенно:
Весь CSS попадает в JS:
main.js = JS + CSS
Отдельный CSS-файл загружается браузером раньше JavaScript.
import('./admin.css');
Webpack создаст отдельный chunk со стилями.
При удалении модуля HMR может автоматически удалять старые стили.
В lazy-режиме:
styles.unuse();
удаляет CSS из DOM.
css-loaderОшибка:
You may need an appropriate loader
Причина:
use: ['style-loader']
без css-loader.
Ошибка:
use: [
'css-loader',
'style-loader'
]
Правильно:
use: [
'style-loader',
'css-loader'
]
Проблемы:
Некорректная конфигурация:
modules: true
для всех файлов.
Правильнее разделять:
test: /\.module\.css$/
и:
test: /\.css$/
exclude: /\.module\.css$/
module.exports = {
module: {
rules: [
{
test: /\.module\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
}
]
},
{
test: /\.css$/i,
exclude: /\.module\.css$/i,
use: [
'style-loader',
'css-loader'
]
}
]
}
};
style-loader работает исключительно в браузерной
среде.
На сервере:
<style> создать невозможно;Для серверного рендеринга обычно применяют:
MiniCssExtractPlugin;Во время выполнения Webpack добавляет runtime-код, который:
<style>;Упрощённо runtime выглядит так:
const style = document.createElement('style');
style.innerHTML = css;
document.head.appendChild(style);
Реальная реализация значительно сложнее и учитывает:
При строгой Content Security Policy может потребоваться
nonce.
{
loader: 'style-loader',
options: {
attributes: {
nonce: 'webpack-nonce'
}
}
}
__webpack_nonce__ = 'random_nonce_value';
Webpack автоматически применит nonce к создаваемым тегам.
В микрофронтенд-архитектуре style-loader часто
используется из-за:
Но возникают проблемы:
Поэтому обычно комбинируют:
| injectType | Особенности |
|---|---|
| styleTag | отдельный <style> на модуль |
| singletonStyleTag | один общий <style> |
| lazyStyleTag | ручное подключение |
| lazySingletonStyleTag | singleton + ручное подключение |
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
}
]
},
devServer: {
hot: true
}
};