Webpack loader 之 style-loader
webpack 本身只能处理 js 文件,而我们项目中 js 文件会依赖的文件还包括 html、css、jpg 等其他类型,自此 loader 就是专门用于将非 JS 模块转换成 JS 模块,同时推荐其单一职责(一个 loader 只负责一种转换)
loader
上面三个文档基本上已经全面的解释了loader
的相关知识,下面罗列一些我们常用的知识点和个人理解:
定义
loader 本质上是导出为函数的 JavaScript 模块。loader runner 会调用此函数,然后将上一个 loader 产生的结果或者资源文件传入进去。
/**
*
* @param {string|Buffer} content 源文件的内容
* @param {object} [map] 可以被 https://github.com/mozilla/source-map 使用的 SourceMap 数据
* @param {any} [meta] meta 数据,可以是任何内容
*/
module.exports = function (content, map, meta) {
// 你的 webpack loader 代码
// 这里我们只是打印一句话并返回 content (也可以是自定义的内容) 给下一个 loader
console.log("This is my first custom loader");
return content;
};
/**
* @remainingRequest 剩余请求: loader链中在自己之后的 loader 的 request 字符串
* @precedingRequest 前置请求 loader链中在自己之前的 loader 的 request字符串
* @data 数据对象
*/
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
// 你的 webpack loader 代码
// 这里处理完后的 data 可在 normal 方法中通过 this.data 得到更新后的值
data.value = 66;
// return something;
};
复制代码
类别
包括 前置(pre)、普通(normal)、行内(inline)、后置(post)
四种类型,其区别则表现为不同的执行顺序
inline
时可通过 !
进行禁用 loader
:
// 禁用普通 loaders
import { a } from "!./file1.js";
// 禁用前置和普通 loaders
import { b } from "-!./file2.js";
// 禁用所有的 laoders
import { c } from "!!./file3.js";
复制代码
不应使用内联 loader 和 ! 前缀,因为它是非标准的。它们可能会被 loader 生成代码使用
-
Pitching 阶段 (自左向右): loader 上的 pitch 方法,按照 后置(post)、行内(inline)、普通(normal)、前置(pre) 的顺序调用。更多详细信息,请查看 [Pitching Loader](webpack.docschina.org/api/loaders…
用处:有些情况下,loader 只关心 request 后面的 元数据(metadata),并且忽略前一个 loader 的结果
-
Normal 阶段 (自右向左): loader 上的 常规方法,按照 前置(pre)、普通(normal)、行内(inline)、后置(post) 的顺序调用。模块源码的转换, 发生在这个阶段。
例如以下配置:
module.exports = {
//...
module: {
rules: [
{
//...
use: ["a-loader", "b-loader", "c-loader"],
},
],
},
};
复制代码
其执行顺序为:
|- a-loader `pitch`
|- b-loader `pitch`
|- c-loader `pitch`
|- requested module is picked up as a dependency
|- c-loader normal execution
|- b-loader normal execution
|- a-loader normal execution
复制代码
而如果我们在上述配置的情况下,b-loader
的 pitch
中返回了一些东西,那么执行顺序又会变为:
|- a-loader `pitch`
|- b-loader `pitch` returns something
|- a-loader normal execution
复制代码
所以当
loader
的pitch
方法中有返回值时,它会跳过剩下的loader
方法(哪怕他们都有pitch
方法),然后回到自己本身并结束
模式
- 同步:
this.callback
- 异步:
this.async
// callback
module.exports = function (content, map, meta) {
const output = someSyncOperation(content);
return output;
// or
this.callback(null, output, map, meta);
return;
};
// async
module.exports = function (content, map, meta) {
const callback = this.async();
someAsyncOperation(content, function (err, result, sourceMaps, meta) {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
};
复制代码
编写自己的 loader
官方建议遵循的原则:webpack.docschina.org/contribute/…
style-loader
基本知识
将 CSS 插入到 DOM,一般与 css-loader(加载 CSS 文件并解析 import 的 CSS 文件,最终返回 CSS 代码) 一起使用
使用
例子
npm init -y
yarn add webpack webpack-cli style-loader css-loader file-loader --save-dev
复制代码
创建几个 css 测试文件:
/* assets/a.css */
.a {
font-size: 16px;
}
复制代码
/* assets/b.css */
.b {
font-size: 16px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
复制代码
/* assets/c.css */
@import url(./a.css);
.c {
display: inline-block;
font-weight: 600;
}
复制代码
入口文件:
// src/index.js
export const add = (num1, num2) => {
return num1 + num2;
};
import "../assets/c.css";
import "../assets/b.css";
// import bCSS from '../assets/b.css';
// bCSS.use();
// bCSS.unuse();
console.log("this is index.js");
console.log(add(66, 99));
复制代码
<!-- dist/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>起步</title>
<!-- <link rel="stylesheet" type="text/css" href="./assets/b.css" /> -->
</head>
<body>
<div class="b">This is webpack loader example</div>
</body>
<script src="main.js"></script>
</html>
复制代码
webpack 配置文件:webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.css$/i,
use: [
// 'style-loader',
{
loader: "style-loader",
options: {
injectType: "styleTag", // 默认值
// injectType: "singletonStyleTag"
// injectType: "lazyStyleTag"
// injectType: "lazySingletonStyleTag"
// injectType: "linkTag" // 以 link 形式挂载到 DOM,此时需要配合 url-loader 而不是 css-loader
},
},
"css-loader",
// 'file-loader'
],
},
],
},
};
复制代码
然后执行 webpack
(也可以在 package.json
的scripts
中配置一个快捷命令 xxx,yarn xxx
)进行打包,接着在浏览器中打开 index.html
可查看打包结果
injectType
可选值:
styleTag
(默认值)、singletonStyleTag
、autoStyleTag
、lazyStyleTag
、lazySingletonStyleTag
、lazyAutoStyleTag
、linkTag
- styleTag
通过使用多个
<style></style>
自动把 styles 插入到 DOM 中,即一个import
就会生成一个style
标签
<style>
.a {
font-size: 16px;
}
</style>
<style>
.c {
display: inline-block;
font-weight: 600;
}
</style>
<style>
.b {
font-size: 16px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
</style>
复制代码
- singletonStyleTag
对应
styleTag
,不过它是把他们整合到一个style
标签中,如有重复的样式名不会额外处理
<style>
.a {
font-size: 16px;
}
.c {
display: inline-block;
font-weight: 600;
}
.a {
font-size: 16px;
}
.b {
font-size: 16px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
</style>
复制代码
- linkTag
使用多个 将 styles 插入到 DOM 中
<link
rel="stylesheet"
href="file:///D:/testCodes/webpack-style-loder/dist/d06ed220ba698dc21e8270035e104dce.css"
/>
<link
rel="stylesheet"
href="file:///D:/testCodes/webpack-style-loder/dist/ada3b1108d67c1ded7467f095ae0a549.css"
/>
复制代码
insert
Type:
String
|Function
, Default:head
默认情况下,除非指定 insert,否则 style-loader 会把 / 添加到页面的 标签尾部。
这会使得 style-loader 创建的 CSS 比 标签内已经存在的 CSS 拥有更高的优先级。 当默认行为不能满足你的需求时,你可以使用其他值,但我们不推荐这么做。
除了 injectType
、insert
参数外,还有 attributes
、esModule
等,详见上述官方文档
感谢封面图:阿宝哥 - 搞懂webpack-loader
预告:style-loader
源码视角看 loader
原理