环境配置
本文按照less-loader、css-loader、style-loader的顺序处理css
创建example文件夹,内容如下
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
devtool: false,
entry: "./src/testLess.js",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
],
},
optimization: {
runtimeChunk: true,
},
};
// package.json
{
"name": "example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^6.7.3",
"html-webpack-plugin": "^5.5.0",
"less-loader": "^11.1.0",
"style-loader": "^3.3.1",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
}
}
// src/test.less
.content {
width: 100%;
&-item {
height: 100%;
}
}
// src/testLess.js
import less from "./test.less";
console.log(less);
断点调试
style-loader
首先进到了style.pitch
request的意思是用 C:\Users\mjgao\css-webpack\example\node_modules\css-loader\dist\cjs.js(就是css-loader)和C:\Users\mjgao\css-webpack\example\node_modules\less-loader\dist\cjs.js(less-loader)去处理C:\Users\mjgao\css-webpack\example\src\test.less这个文件
之后进入到字符串拼接,拼接出来的字符串内容如下
import API from "!../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js";
import domAPI from "!../node_modules/style-loader/dist/runtime/styleDomAPI.js";
import insertFn from "!../node_modules/style-loader/dist/runtime/insertBySelector.js";
import setAttributes from "!../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js";
import insertStyleElement from "!../node_modules/style-loader/dist/runtime/insertStyleElement.js";
import styleTagTransformFn from "!../node_modules/style-loader/dist/runtime/styleTagTransform.js";
import content, * as namedExport from "!!../node_modules/css-loader/dist/cjs.js!../node_modules/less-loader/dist/cjs.js!./test.less";
var options = {};
options.styleTagTransform = styleTagTransformFn;
options.setAttributes = setAttributes;
options.insert = insertFn.bind(null, "head");
options.domAPI = domAPI;
options.insertStyleElement = insertStyleElement;
var update = API(content, options);
export * from "!!../node_modules/css-loader/dist/cjs.js!../node_modules/less-loader/dist/cjs.js!./test.less";
export default content && content.locals ? content.locals : undefined;
由于pitch返回了这段内容,此次loader过程结束,本文结束
虽然loader结束了,但是webpack在生成上面这段代码的时候,会分析内容
仔细看这段代码,这些import是webpack的内联loader,感叹号代表对这个文件禁用loader(不然webpack会对这个文件也进行loader操作) webpack.docschina.org/configurati…
上面这段代码很明显指明了用css-loader和less-loader去处理test.less这个文件
所以此时会执行第二次loader————内联loader
由于css-loader和less-loader并没有pitch,所以执行loader函数,首先执行less-loader
less-loader
简略代码,去掉日志,sourcemap等等其他内容
async function lessLoader(source) {
const options = this.getOptions(_options.default);
const callback = this.async();
let result;
try {
result = await implementation.render(data, lessOptions);
} catch (error) {
if (error.filename) {
this.addDependency(_path.default.normalize(error.filename));
}
callback(new _LessError.default(error));
return;
}
const {
css,
imports
} = result;
imports.forEach(item => {
if ((0, _utils.isUnsupportedUrl)(item)) {
return;
}
const normalizedItem = _path.default.normalize(item);
if (_path.default.isAbsolute(normalizedItem)) {
this.addDependency(normalizedItem);
}
});
callback(null, css);
}
可以看到,传进去的source就是test.less的内容
const callback = this.async()这里用了异步的回调
result = await implementation.render(data, lessOptions);这里是loader的处理
处理完的结果就是css内容了
imports.forEach是将每个less文件都监听,因为less-loader会整合所有的less,若单个文件改动,需要全部重新构建一遍,
在开发过程中会发现如果文件出错了,修改后编译得非常快,这是因为抛出异常的时候,监听了这个文件
最后异步调用callback,将处理结果传给webpack
css-loader
可以看见content就是less-loader处理后的内容
由于less-loader的callback是异步的,所以css-loader也需要异步
css-loader的作用就是将css处理成js
// Imports
import ___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___ from "../node_modules/css-loader/dist/runtime/noSourceMaps.js";
import ___CSS_LOADER_API_IMPORT___ from "../node_modules/css-loader/dist/runtime/api.js";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_NO_SOURCEMAP_IMPORT___);
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".content {\n width: 100%;\n}\n.content-item {\n height: 100%;\n}\n", ""]);
// Exports
export default ___CSS_LOADER_EXPORT___;
这就是css-loader的处理结果,最后生成的结果还要再经过webpack的处理
总结
style-loader的作用就是生成一份字符串代码给webpack,此时loader就结束了,后面webpack分析引用的时候发现这份代码还存在调用loader,于是就再一次启用loader
后面网页请求的时候调用style-loader生成的那段代码,所有的东西就都能访问到了
这是执行main.js的时候的html
var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_node_modules_less_loader_dist_cjs_js_test_less__WEBPACK_IMPORTED_MODULE_6__["default"], options);
执行到这句之后,页面就有style标签了
对应的应该是字符串代码var update = API(content, options);