Оригинальный адрес:Руководство по исходному коду Webpack Loader css-loader
в предыдущем постеРуководство по исходному коду Webpack LoaderПредставляем меньше загрузчика
Эта статья представляет собой интерпретацию css-loader из серии руководств по исходному коду Webpack Loader, в основном описывает работу загрузчика и роль некоторых элементов конфигурации.
структура исходного кода
Исходный код v0.28.8, каталог lib выглядит следующим образом:
lib
|____compile-exports.js
|____createResolver.js
|____css-base.js
|____getImportPrefix.js
|____getLocalIdent.js
|____loader.js
|____localsLoader.js
|____processCss.js
входной файл
css-loader имеет два входных файлаlib/loader.js
а такжеlib/localsLoader.js
Обзор элемента конфигурации
имя | Типы | По умолчанию | описывать |
---|---|---|---|
root | String | / | Путь для разрешения URL-адреса, URL-адреса, начинающиеся с /, не будут переведены |
url | Boolean | true | Включить/выключить обработку url() |
alias | Object | {} | Создайте псевдонимы, чтобы упростить импорт некоторых модулей |
import | Boolean | true | Включить/выключить обработку @import |
модули или модули | Boolean | false | Включить/отключить модули CSS |
sourceMap | Boolean | false | Включить/отключить исходную карту |
camelCase | Логическое значение или строка | false | Имена классов экспорта CamelCase |
importLoaders | Number | 0 | Количество загрузчиков, примененных перед css-loader |
localIdentName | String | [hash:base64] | Настройте сгенерированный идентификатор (ident) |
Роль каждого элемента конфигурации мы приведем на примере, чтобы проиллюстрировать роль в процессе чтения кода ниже;
processCss
Будь то loader.js или localsLoader.js, сначала будет проанализирован параметр загрузчика, а затем будет выполнен processCss для компиляции файла css. Разница между ними в том, что результат компиляции не может быть обработан. Сначала давайте посмотрим что делает processCss.
Давайте запустим пример b.css
@value colorYellow: yellow;
:local(.className) {
background: red;
color: colorYellow;
}
:local(.subClass) {
composes: className;
background: blue;
}
a.css
@value colorYellow from './b.css';
:local(.aClass) {
composes: className from './b.css';
background: colorYellow;
}
.app {
font-size: 14px;
}
Конфигурация загрузчика, для просмотра результатов компиляции мы настроили extract-text-webpack-plugin
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: "css-loader",
options: {
minimize: true,
sourceMap: true,
modules: true,
localIdentName: '[hash:base64]'
}
}
]
})
}
Первый - после волны postcss (v5.2.17) обработка, Сначала проанализируйте входное содержимое (код находится в postcss/lib/parser.js), извлеките некоторые ключевые слова, преобразуйте их в указанные объекты для последующего анализа и преобразуйте их в объекты в следующем формате:
{
"raws": {
"semicolon": false,
"after": "\n"
},
"type": "root",
"nodes": [
{
"raws": {
"before": "",
"between": "",
"afterName": " "
},
"type": "atrule",
"name": "value",
"source": {
"start": {
"line": 1,
"column": 1
},
"input": {
"css": "@value colorYellow from './b.css';\n\n:local(.aClass) {\n composes: className from './b.css';\n background: colorYellow;\n}\n\n.app {\n font-size: 14px;\n}\n",
"file": "/css-loader!/Users/yzf/webpack-tuition/loaders/babel/src/a.css"
},
"end": {
"line": 1,
"column": 34
}
},
"params": "colorYellow from './b.css'"
},
...
],
"source": {
"input": {
"css": "@value colorYellow from './b.css';\n\n:local(.aClass) {\n composes: className from './b.css';\n background: colorYellow;\n}\n\n.app {\n font-size: 14px;\n}\n",
"file": "/css-loader!/Users/yzf/webpack-tuition/loaders/babel/src/a.css"
},
"start": {
"line": 1,
"column": 1
}
}
}
Типы узлов включают root (с узлом), atrule (@
Rules), decl (декларация), comment (комментарий) и rule (обычные правила) нескольких типов
Затем этот узел будет обрабатываться серией плагинов.Во время обработки плагина вы часто будете видеть несколько методов walkAtRules, walkRules, walkDecls и walkComments.Коды этих методов находятся вpostcss/lib/container.js
середина,
Как следует из названия, эти методы используются для анализа этих различных правил, таких какwalkAtRules('value',callback)
значит разобрать@value
правило
В css-loader используются следующие плагины
var pipeline = postcss([
modulesValues,
localByDefault({
mode: options.mode,
rewriteUrl: function(global, url) {
if(parserOptions.url){
url = url.trim();
if(!url.replace(/\s/g, '').length || !loaderUtils.isUrlRequest(url, root)) {
return url;
}
if(global) {
return loaderUtils.urlToRequest(url, root);
}
}
return url;
}
}),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName (exportName) {
return customGetLocalIdent(options.loaderContext, localIdentName, exportName, {
regExp: localIdentRegExp,
hashPrefix: query.hashPrefix || "",
context: context
});
}
}),
parserPlugin(parserOptions)
]);
Далее, давайте посмотрим, что делают эти плагины.
Первый плагин — это modulesValues(postcss-modules-values v1.3.0), его роль заключается в разборе переменной @value, как в b.css
Определенный@value colorYellow: yellow;
можно использовать позжеcolor: colorYellow;
, эффект тот жеcolor: yellow;
, в a.css вы также можете импортировать значение из b.css@value colorYellow from './b.css';
;
Второй плагин - localByDefault(postcss-modules-local-by-default v1.2.0), функция этого плагина связана с модулями элементов конфигурации css-loader;
Если для модулей установлено значение true, плагин будет добавлять к каждому имени класса префикс:local
, так в jsimport s from './a.css'
Значение s, полученное, когда{ colorYellow: 'yellow', aClass: '_3RfWl8Fjg9j10HraIxvVwo _2WlYzvzC-urSx4y6mIOOFM', app: '_2fkqRy5LeEcw20RyY_eLpM' }
,
иначе{ colorYellow: 'yellow', aClass: '_3RfWl8Fjg9j10HraIxvVwo _2WlYzvzC-urSx4y6mIOOFM' }
; Разница в классе приложения в .css, в примере кода.app
не добавлено ранее:local
Тогда экспортируемый объект не содержит приложения,
Но когда для модулей установлено значение true, этот плагин по умолчанию добавит локальный к приложению, поэтому в экспортируемом объекте есть приложение.
Третий плагин — extractImports(postcss-modules-extract-imports v1.1.0), посмотрите на код в .css, функция этого плагина заключается в том, чтобы
:local(.aClass) {
composes: className from './b.css';
background: colorYellow;
}
Превратиться в
:import("./b.css"){
className: i__imported_className_0;
}
:local(.aClass) {
composes: i__imported_className_0;
background: colorYellow;
}
Четвертый плагин — modulesScope (postcss-modules-scope v1.1.0), функция этого плагина состоит в том, чтобы экспортировать объекты, которые можно импортировать в js, что будет
:local(.aClass) {
composes: i__imported_className_0;
background: colorYellow;
}
Превратиться в
:export {
aClass: _3RfWl8Fjg9j10HraIxvVwo
}
._3RfWl8Fjg9j10HraIxvVwo {
composes: i__imported_className_0;
background: colorYellow;
}
Композиции здесь пока рассматриваться не будут. Преобразованное имя класса, например_3RfWl8Fjg9j10HraIxvVwo
по пункту комплектацииlocalIdentName: '[hash:base64]'
определяется, если конфигурацияlocalIdentName: '[local]'
, имя класса не изменится, т.е.aClass
.
Последний плагин — это parserPlugin, этот код находится вcss-loader/src/processCss.js
, это окончательная обработка, выполняемая css-loader над предыдущим результатом компиляции.
Добавляем c.css в демо, а потом импортируем в .css@import "./c.css"
, этот плагин делает следующее:
- Если настроен import: true (по умолчанию true), правило @import анализируется, URL-адрес импортируемого модуля извлекается в соответствии с конфигурацией options.root и временно сохраняется в importItems;
- пройти через
var icss = icssUtils.extractICSS(css);
Извлеките информацию :import и :export каждого файла из узлов и временно сохраните содержимое :import в imports и importItems.
// imports
{
"$i__const_colorYellow_0": 1, // 值为importItems中的索引
"$i__imported_className_0": 2
}
// importItems
[
{
"url": "./c.css",
"mediaQuery": ""
},
{
"url": "./b.css",
"export": "colorYellow"
},
{
"url": "./b.css",
"export": "className"
}
]
а затем экспорт из imports и importItems
{
"colorYellow": "i__const_colorYellow_0",
"aClass": "_3RfWl8Fjg9j10HraIxvVwo i__imported_className_0",
"app": "_2fkqRy5LeEcw20RyY_eLpM"
}
Перевести в
{
"colorYellow": "___CSS_LOADER_IMPORT___1___", // 1,2即为importItems中的索引
"aClass": "_3RfWl8Fjg9j10HraIxvVwo ___CSS_LOADER_IMPORT___2___",
"app": "_2fkqRy5LeEcw20RyY_eLpM"
}
- Объявить значение узла в узлах
i__const_colorYellow_0
заменены на___CSS_LOADER_IMPORT___1___
Форма;
После того, как все плагины обработаны, результат такой (конечно, посередине есть сжатие cssnano, когда для минимизации установлено значение true, которое здесь пропущено):
/* a.css */
._3RfWl8Fjg9j10HraIxvVwo{background:___CSS_LOADER_IMPORT___1___}._2fkqRy5LeEcw20RyY_eLpM{font-size:14px}
/* b.css */
._2WlYzvzC-urSx4y6mIOOFM{background:red;color:#ff0}._2ZjxOCWmD5GtQv4c-EHJ1g{background:blue}
/* c.css */
._2W2YIQ3PA5I9QGXroo7b2m{display:block}
окончательная обработка загрузчика
Для приведенных выше результатов обработки имеется также___CSS_LOADER_IMPORT___1___
Такой контент, явно не окончательный результат, вернемся к нашему входному файлуloader.js
Посмотрите на окончательную обработку,
Конечно, если используемый вами загрузчикcss-loader/locals
, Это входной файлlocalsLoader.js
.
Последнее, что должен сделать loader.js, — указать модули, которые будут экспортированы последним module.exports, и передать зависимые модули через регулярные выражения./___CSS_LOADER_IMPORT___([0-9]+)___/g
и ранее проанализированныйimportItems
заменитьrequire("-!../node_modules/css-loader/index.js?{\"minimize\":true,\"sourceMap\":true,\"modules\":true,\"localIdentName\":\"[hash:base64]\"}!./b.css").locals["colorYellow"]
форматированный
Окончательный модуль экспорта можно напрямую импортировать в js, чтобы получить объект
/* a.css */
exports = module.exports = require("../node_modules/css-loader/lib/css-base.js")(true);
// imports
exports.i(require("-!../node_modules/css-loader/index.js?{\"minimize\":true,\"sourceMap\":true,\"modules\":true,\"localIdentName\":\"[hash:base64]\"}!./c.css"), "");
exports.i(require("-!../node_modules/css-loader/index.js?{\"minimize\":true,\"sourceMap\":true,\"modules\":true,\"localIdentName\":\"[hash:base64]\"}!./b.css"), undefined);
// module
exports.push([module.id, "._3RfWl8Fjg9j10HraIxvVwo{background:"
+ require("-!../node_modules/css-loader/index.js?{\"minimize\":true,\"sourceMap\":true,\"modules\":true,\"localIdentName\":\"[hash:base64]\"}!./b.css").locals["colorYellow"]
+ "}._2fkqRy5LeEcw20RyY_eLpM{font-size:14px}", "", {"version":3,"sources":["/Users/yzf/webpack-tuition/loaders/babel/src/a.css"],"names":[],"mappings":"AAGA,yBAEI,sCAAwB,CAC3B,AAED,yBACI,cAAgB,CACnB","file":"a.css","sourcesContent":["@import \"./c.css\";\n@value colorYellow from './b.css';\n\n:local(.aClass) {\n composes: className from './b.css';\n background: colorYellow;\n}\n\n.app {\n font-size: 14px;\n}\n"],"sourceRoot":""}]);
// exports
exports.locals = {
"colorYellow": "" + require("-!../node_modules/css-loader/index.js?{\"minimize\":true,\"sourceMap\":true,\"modules\":true,\"localIdentName\":\"[hash:base64]\"}!./b.css").locals["colorYellow"] + "",
"aClass": "_3RfWl8Fjg9j10HraIxvVwo " + require("-!../node_modules/css-loader/index.js?{\"minimize\":true,\"sourceMap\":true,\"modules\":true,\"localIdentName\":\"[hash:base64]\"}!./b.css").locals["className"] + "",
"app": "_2fkqRy5LeEcw20RyY_eLpM"
};
/* b.css */
exports = module.exports = require("../node_modules/css-loader/lib/css-base.js")(true);
// imports
// module
exports.push([module.id, "._2WlYzvzC-urSx4y6mIOOFM{background:red;color:#ff0}._2ZjxOCWmD5GtQv4c-EHJ1g{background:blue}", "", {"version":3,"sources":["/Users/yzf/webpack-tuition/loaders/babel/src/b.css"],"names":[],"mappings":"AAEA,yBACI,eAAgB,AAChB,UAAmB,CACtB,AAED,yBAEI,eAAiB,CACpB","file":"b.css","sourcesContent":["@value colorYellow: yellow;\n\n:local(.className) {\n background: red;\n color: colorYellow;\n}\n\n:local(.subClass) {\n composes: className;\n background: blue;\n}\n"],"sourceRoot":""}]);
// exports
exports.locals = {
"colorYellow": "yellow",
"className": "_2WlYzvzC-urSx4y6mIOOFM",
"subClass": "_2ZjxOCWmD5GtQv4c-EHJ1g _2WlYzvzC-urSx4y6mIOOFM"
};
/* c.css */
exports = module.exports = require("../node_modules/css-loader/lib/css-base.js")(true);
// imports
// module
exports.push([module.id, "._2W2YIQ3PA5I9QGXroo7b2m{display:block}", "", {"version":3,"sources":["/Users/yzf/webpack-tuition/loaders/babel/src/c.css"],"names":[],"mappings":"AAAA,yBACI,aAAe,CAClB","file":"c.css","sourcesContent":[".test {\n display: block;\n}\n"],"sourceRoot":""}]);
// exports
exports.locals = {
"test": "_2W2YIQ3PA5I9QGXroo7b2m"
};
/* 合并后main.css */
._2W2YIQ3PA5I9QGXroo7b2m{display:block}._2WlYzvzC-urSx4y6mIOOFM{background:red;color:#ff0}._2ZjxOCWmD5GtQv4c-EHJ1g{background:blue}._3RfWl8Fjg9j10HraIxvVwo{background:yellow}._2fkqRy5LeEcw20RyY_eLpM{font-size:14px}
/*# sourceMappingURL=main.css.map*/
Если он используется на стороне сервераcss-loader/locals
Если он не совпадает с ExtractTextPlugin, результат обработки
/* a.css */
module.exports = {
"colorYellow": "" + require("-!../node_modules/css-loader/locals.js??ref--1-0!./b.css")["colorYellow"] + "",
"aClass": "_3RfWl8Fjg9j10HraIxvVwo " + require("-!../node_modules/css-loader/locals.js??ref--1-0!./b.css")["className"] + "",
"app": "_2fkqRy5LeEcw20RyY_eLpM"
};
/* b.css */
module.exports = {
"colorYellow": "yellow",
"className": "_2WlYzvzC-urSx4y6mIOOFM",
"subClass": "_2ZjxOCWmD5GtQv4c-EHJ1g _2WlYzvzC-urSx4y6mIOOFM"
};
c.css не имеет экспорта модулей
резюме
Этот процесс парсинга немного длинный, но css-loader в основном упомянул основной процесс обработки css.Мы также можем знать, как будет выглядеть стиль после этого загрузчика и какие модули экспортируются;
Конечно, такие функции, как @import @value composes, упомянутые выше, нельзя использовать при использовании других прекомпиляторов CSS, таких как less или sass, потому что они имеют собственный синтаксис, и мы обычно не используем эти функции;
После того, как загрузчик css будет обработан, мы пройдем через загрузчик стилей в конце фактического использования, иногда с ExtractTextPlugin, так что же делают эти два загрузчика или плагина? Увидимся в следующий раз.
Если вам это нравится, пожалуйста, ставьте лайк и следите за моим блогомhiihl.