前言
webpack 不仅有打包功能,还能对项目中各种文件按照我们的需求进行处理,这就用到了 loader,所谓 loader 就是集成到 webpack 的文件处理方法,这些 loader 在 webpack 打包过程中,可以对指定类型的文件进行相应的处理,比如把 less 语法转换成浏览器可以识别的 css 语法,引入特定类型的文件(html)等等。下面将介绍一下一系列常用的 loader,比如less-loader,css-loader,style-loader,随便上一段常用配置:
const lessLoader = {
test: /.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
'less-loader',
],
}
以上文件就是告诉 webpack,在遇到文件名以.less 结尾的文件,先用 less-loader 编译,再用 css-loader 编译,最后用 style-loader,曾经我是用这样一段 js 伪代码来概述的:
// 伪代码
const res = styleLoader(cssLoader(lessLoader('xxx.less')));
其实这个伪代码,一点都不严谨.
都是 loader,但作用却大相径庭
style-loader 其实与 css-loader, less-loader的作用是有区别的,后者其实承担的是模块化与语法转译这一块;而style-loader这一类(还有常用的 mini-css-extract-plugin)承担的是粘结剂功能,就是将 js 中的 css 加载到 html 中,从而使样式生效;

经过 debug,发现样式确实被打包进了文件,只是没有被加载到 style 标签中,第一直觉就是缺少 loader,而后查看构建工具,发现样式编译配置只到了css-loader这一级,缺少style-loader。
从构建结果看样式加载过程
如果到这文章就结束了,似乎就太不像我了,毕竟刨根问底的我才是真的我。接下来,通过一段实例代码,来看 webpack 构建,是怎样实现样式加载的。
示例代码:
// style.less 代码
@font-size: 28px;
.Demo {
box-sizing: border-box;
:global {
.demo-title {
font-size: @font-size;
}
}
}
import React from 'react';
// 引入样式
import style from './style.less';
export default function Demo() {
return (
<div className={style.Demo}>
<h3 className="emo-title">this is a demo</h3>
</div>
);
}
然后通过 webpack,采用style-loader的方式构建,为了方便阅读,没有做代码压缩和丑化。
然后从入口可以看见样式引入是这样的:
// 注释无用代码有删减
__webpack_require__.d(__webpack_exports__, "default", function() { return Demo; });
var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
// 引入样式
var _style_less__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/demo/style.less");
var _style_less__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_style_less__WEBPACK_IMPORTED_MODULE_1__);
function Demo() {
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: _style_less__WEBPACK_IMPORTED_MODULE_1___default.a.Demo
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h3", {
className: "emo-title"
}, "this is a demo"));
}
可以看见,在入口文件,有一个从./src/demo/style.less模块的样式引入,并赋值给了_style_less__WEBPACK_IMPORTED_MODULE_1___default变量;
然后顺藤摸瓜,继续看一下 style.less 模块长什么样:
// "./src/demo/style.less" 模块
function(module, exports, __webpack_require__) {
var api = __webpack_require__("../../../../.def/def_modules/.builders/@ali/builder-cook/node_modules/_style-loader@1.3.0@style-loader/dist/runtime/injectStylesIntoStyleTag.js");
var content = __webpack_require__("../../../../.def/def_modules/.builders/@ali/builder-cook/node_modules/_css-loader@1.0.1@css-loader/index.js?!../../../../.def/def_modules/.builders/@ali/builder-cook/node_modules/_postcss-loader@3.0.0@postcss-loader/src/index.js?!../../../../.def/def_modules/.builders/@ali/builder-cook/node_modules/_less-loader@6.2.0@less-loader/dist/cjs.js?!./src/demo/style.less");
content = content.__esModule ? content.default : content;
if (typeof content === 'string') {
content = [[module.i, content, '']];
}
var options = {};
options.insert = "head";
options.singleton = false;
var update = api(content, options);
module.exports = content.locals || {};
}
这一块的代码就可以看出,这一个模块是一个承上(css 引入)启下(插入 html);
承上通过从less-loader/dist/cjs.js?!./src/demo/style.less这个模块引入, 而插入 html 则是通过从injectStylesIntoStyleTag导入了一个方法,从方法名就可以知道他的作用就是通过 style 标签插入 css 样式.
这里面还有一个点,就是最后的导出,模块最后将 content.locals 导出,继续根据线索去看看 content 到底是什么:
function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__("../../../../.def/def_modules/.builders/@ali/builder-cook/node_modules/_css-loader@1.0.1@css-loader/lib/css-base.js")(false);
exports.push([module.i, "._1rfoVjSya7b-iuQB2_qKPh {n -webkit-box-sizing: border-box;n box-sizing: border-box;n}n._1rfoVjSya7b-iuQB2_qKPh .demo-title {n font-size: 28px;n}n", ""]);
exports.locals = {
"Demo": "_1rfoVjSya7b-iuQB2_qKPh"
};
}
通过上面的代码,就可以看出exports.locals的作用就是 css 的模块化。
至此,真相大白….