Руководство по исходному коду Webpack Loader css-loader

JavaScript CSS Webpack PostCSS

Оригинальный адрес:Руководство по исходному коду 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.