我正在参加「掘金·启航计划」
Webpack 是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。本文主要写了Webpack的常用的 Css Loader 使用以及实现自定义 Loader。
Webpack 系列专栏地址 进入
本文demo地址查看
less-loader
安装的包名后缀带
-loader
的基本都是是webapck生态的插件,如果不用最新版本的webapck,一般都需要锁定版本,和webpack的版本要兼容。
安装:
npm install less less-loader@7 -D
配置项:
module: {
rules: [
// ...
{
test: /\.less$/,
use: [minicssPlugin.loader, "css-loader", "less-loader"],
},
],
},
多个loader作用于同一个模块时,按从后到前的顺序执行:
less-loader
处理less
文件解析为css
文件css-loader
将css 代码序列化minicssPlugin.loader
负责抽取样式 生成独立的css文件
postcss-loader
安装:
npm install postcss postcss-loader@4 -D
配置:
{
test: /\.less$/,
use: [
minicssPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader",
],
},
将postcss-loader
配置项放到 less-laoder
的前面,由less-loader
处理得到的css文件再交给postcss-loader
。
下面安装postcss
的插件 autoprefixer,使用 autoprefixer 添加厂商前缀。解决css语法的一些兼容性问题。
安装:
npm install autoprefixer -D
新建postcss.config.js为postcss的配置文件:
module.exports = {
plugins: [require("autoprefixer")],
};
此时,执行打包命令,发现css文件并没有变化。
这是因为还需要配置browserslist
声明目标浏览器集合的工具。
我们用到的需要兼容的工具,比如
babel
autoprefixer
会通过browserslist
声明的浏览器集合,针对性的输出兼容性的代码。
在 package.json
中写入配置:
"browserslist": [
"last 2 versions",
">1%"
]
上述配置表示:兼容所有浏览器最后两个版本,浏览器的市场占有率大于1%。
再次执行打包命令,发现css样式被添加了一些兼容性代码。
cssnano
cssnano
是一个模块化的 CSS 压缩器。
安装:
npm install cssnano -D
配置:
// postcss.config.js
module.exports = {
plugins: [require("autoprefixer"), require("cssnano")],
};
观察打包后的css代码,发现代码被压缩。
关于browserslist
browserslist
实际上就是声明了⼀段浏览器的集合,我们的⼯具可以根据这段集合描述,针对性的输出 兼容性代码。
配置
Browserslist 的配置可以放在 package.json
中,也可以单独放在配置⽂件 .browserslistrc
中。所 有的⼯具都会主动查找 browserslist
的配置⽂件,根据 browserslist
配置找出对应的⽬标浏览器集合。
Browserslist 的数据都是来⾃Can I Use:browserl.ist/
但是目前这个⽹站关闭了,现在需要⼿动检测:
npx browserslist "last 1 version, >1%"
常见集合范围说明
范围 | 说明 |
---|---|
last 2 versions | 浏览器最新两个版本,如chrome90 和 chrom91 |
> 1% | 全球超过 1%⼈使⽤的浏览器 |
cover 99.5% | 覆盖 99.5%主流浏览器 |
chrome > 50 或ie 6-8 | 指定某个浏览器版本范围 |
not ie < 11 | 排除 ie11 以下版本不兼容 |
current node | 当前环境的 node 版本 |
dead | 通过 last 2 versions 筛选的浏览器中,全球使⽤率低于 0.5% 且官⽅声明 不在维护或者事实上已经两年没有再更新的版本 |
defaults | 默认配置, > 0.5% last 2 versions Firefox ESR not dead |
实现自定义 loader
通过前面的学习,可以了解到webpack打包项目时遇到非js 非json的模块,可以在webpack.config.js
配置文件中通过module
去配置,根据配置规则,将相应的模块给对应的loader
去处理,并将loader
编译后的内容再交给webpack
进一步处理。
下面将手写实现一个loader:
新建 /myLoaders/k-loader.js
webpack.config.js配置文件:
module: {
rules: [
// ...
{
test: /\.js$/,
use: [
resolve(__dirname, "./myLoaders/k-loader.js")
],
},
],
}
use
自定义的loader,需要使用 resolve
获取路径,不能直接写包名。
编写loader的几个要点:
- 结构是一个函数,但不可以是箭头函数。
- 必须有返回值,必须是String 或 Buffer
- 使用
this.query
loader API 接收options
配置项 - 使用
this.callback
返回多个信息 - loader需要对异步逻辑进行特殊处理
- 多个 loader 如何配置
k-loader.js:
module.exports = function (source) {
console.log(source);
return source.replace("hello", "你好");
};
source
参数就是接受的模块文件,先简单测试一下,将hello
替换成你好
。执行打包命令,查看bundle文件,发现成功被替换。
this.query
添加options
配置项:
// webapck.config.js
{
test: /\.js$/,
// use: [resolve(__dirname, "./myLoaders/k-loader.js")],
use: [
{
loader: resolve(__dirname, "./myLoaders/k-loader.js"),
options: { title: "你好" },
},
],
},
在 k-loader.js
中接收 options
:
module.exports = function (source) {
console.log(source);
// return source.replace("hello", "你好");
return source.replace("hello", this.query.title);
};
this.query 这个loader API,可以获取到options
对象。执行打包命令,bundle文件中hello
被替换成title
的值。
this.callback
this.callback 用于返回多个信息。有同步和异步两种调用方式。
k-loader.js:
module.exports = function (source) {
console.log(source);
// return source.replace("hello", "你好");
// return source.replace("hello", this.query.title);
const info = source.replace("hello", this.query.title);
this.callback(null, info);
};
再次打包发现结果无差异。
处理异步逻辑
this.async 告诉 loader-runner 这个 loader 将会异步地回调。返回值是 this.callback
。
k-loader.js:
module.exports = function (source) {
// 处理异步逻辑
const callback = this.async();
setTimeout(() => {
const info = source.replace("hello", this.query.title);
callback(null, info);
}, 3000);
};
多个 loader 配置
新建 l-loader.js
module.exports = function (source) {
return source.replace("webpack", "webpack!!!");
};
增加 webpack.config.js 配置:
{
test: /\.js$/,
// use: [resolve(__dirname, "./myLoaders/k-loader.js")],
use: [
{
loader: resolve(__dirname, "./myLoaders/k-loader.js"),
options: { title: "你好" },
},
// 第二个loader配置
{
loader: resolve(__dirname, "./myLoaders/l-loader.js"),
},
],
},
再次打包,index.js
模块被打包后输出了 你好 webpack!!!
,这也是我们预期的结果。
resolveLoader
resolveLoader
用于解析 webpack 的 loader包,可以简化引入自定义loader的方式。
resolveLoader
中的modules
数组拥有一个默认值node_modules
,所以我们通过npm安装的loader,可以直接写loader的名称,会自动在node_module
下搜索。那么,把我们自定义loader的路径添加进去,也可以实现在我们添加的路径下自动查找loader,这时候就不用再写完整的路径使用loader了。
webpack.config.js:
resolveLoader: {
modules: ["node_modules", "./myLoaders"],
},
// 简化路径
{
test: /\.js$/,
use: [
{
// loader: resolve(__dirname, "./myLoaders/k-loader.js"),
loader: "k-loader",
options: { title: "你好" },
},
{
// loader: resolve(__dirname, "./myLoaders/l-loader.js"),
loader: "l-loader",
},
],
},
实现 mini 版的 处理 less 相关loader
新建下列文件
- mini-style-loader.js
- mini-css-loader.js
- mini-less-loader.js
webpack.config.js配置:
{
test: /\.less$/,
use: [
// minicssPlugin.loader,
// "css-loader",
// "postcss-loader",
// "less-loader",
// 替换成自己手写实现的loader
"mini-style-loader",
"mini-css-loader",
"mini-less-loader",
],
},
mini-less-loader
使用 less
模块将 .less
文件解析为css的代码。
const less = require("less");
module.exports = function (source) {
less.render(source, (error, { css }) => {
this, callback(error, css);
});
};
mini-css-loader
将 css 序列化。
module.exports = function (source) {
return JSON.stringify(source);
};
mini-style-loader
- 操作 DOM,创建 style
module.exports = function (source) {
return `
const tag = document.createElement("style")
tag.innerHTML = ${source}
document.head.appendChild(tag)
`;
};
返回字符串的形式,经过webpack的处理会把这个字符串交给eval
函数执行。
最后执行打包命令,运行html文件,可以正常显示效果。