看一下webpack官方对loader的描述文档: webpack.js.org/contribute/…
A loader is a node module that exports a function. This function is called when a resource should be transformed by this loader. The given function will have access to the Loader API using the this context provided to it.
就感觉很高级
上面这三句话,有三个知识点:
- loader是一个function,使用commonjs导出。(模块管理、导出类型)
- loader的作用是转换资源。(函数需要返回值)
- loader这个函数需要使用this上下文,来使用LoaderAPI提供的接口。(不能是箭头函数)
那么可以确定,代码层级的loader应该是下面的样子
module.exports = function (source) {
// ...do something
return output
}
其实上一篇《Webpack4散记(4)JS和CSS的打包优化》中提到的,最终使用MiniCSS和OptimizeCss来实现CSS代码压缩的方案,自我感觉并不理想,原因如下:
- 代码剥离和代码压缩两个行为合并,偏离最初的纯压缩目标。
- 代码包数量double,导致页面加载阶段并发请求过多。
- 使用
@loadable/components时,按需加载的包也double了,不好管理,而且丑。
今天又找了一波资料,搜了npm,并未发现自己所需的工具。所以决定自己动手搞一个。
编写css-min-loader代码
对代码的处理,一定是CODE -> AST -> CODE这个路径,不要考虑正则或者字符串替换之类。
对CSS解析OOM的工具一大把,找个元老级的css包:www.npmjs.com/package/css
文档中提到:
css.stringify(object, [options])
关于第二个参数options
options:
- indent: the string used to indent the output. Defaults to two spaces.
- compress: omit comments and extraneous whitespace.
- ...
那么事情就简单了,直接撸代码:
const css = require('css')
module.exports = function (source) {
const ast = css.parse(source)
const min = css.stringify(ast, { compress: true, indent: '' })
return min
}
将自定义loader嵌入webpack
在webpack中,loader的调用是通过require方式;每个loader配置,一定少不了用于require的调用路径。
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: path.resolve(__dirname, './css-min-loader.js')
}
],
exclude: /node_modules/,
},
{
test: /\.less$/i,
use: [
'style-loader',
'css-loader',
{
loader: path.resolve(__dirname, './css-min-loader.js')
},
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
// strictMath: true,
}
},
}
],
},
再啰嗦一句:一组loader的调用顺序,是从下向上的。比如上面的css组,是
css-min-loader -> css-loader -> style-loader
的顺序;而less组是:
less-loader -> css-min-loader -> css-loader -> style-loader
换句话说,css-min-loader需要传入原始的css代码,将压缩好的代码传给后续的loader处理。因为后续loader也都遵循基于AST处理的方式,所以对后续而言,css-min-loader的压缩是没有副作用的。
未使用 css-min-loader
Asset Size Chunks Chunk Names
antd.8c2d.js 99.3 KiB 0 [emitted] [immutable] antd
client.ca88.js 23.8 KiB 1 [emitted] [immutable] client
common.fb46.js 136 KiB 2 [emitted] [immutable] common
index.html 208 bytes [emitted]
Entrypoints:
client (259 KiB)
common.fb46.js
antd.8c2d.js
client.ca88.js
使用 css-min-loader
Asset Size Chunks Chunk Names
antd.564f.js 86.9 KiB 0 [emitted] [immutable] antd
client.faf7.js 23.7 KiB 1 [emitted] [immutable] client
common.fb46.js 136 KiB 2 [emitted] [immutable] common
index.html 208 bytes [emitted]
Entrypoints:
client (247 KiB)
common.fb46.js
antd.564f.js
client.faf7.js
可以看到,体积降低了大约12KiB。
关于loader的编写,就到这里。
后续
再次检查输出产物,发现css对代码的压缩效果并不是十分理想,对带有prefix的样式、rgba或cubic-bezier后的值,并没有做到最佳优化。也有朋友推荐cssnano,有待进一步尝试。
另外,css-min-loader已经发布到npm,也就是说可以直接使用:
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'css-min-loader' // 这里
],
},
来配置webpack了。上面提到的压缩优化的问题,也会在这个项目里继续改进。
NPM: www.npmjs.com/package/css…
以上。