style-resources-loader 利用
此 loader 将款式资源(例如变量、mixin)注入到 sass、scss、less、stylus 等模块中
通过在工作中的利用场景,来介绍此 loader 用法和一些发散的点
场景需要
我的项目构造如下:
├─views
├─componets 组件目录
| ├─brand1 组件 1
| ├─cmp.vue
| ├─...
| └─brand2 组件 2
| ├─cmp.vue
| ├─...
├─theme 主题目录
(每个主题中色彩变量可能会反复)
| ├─brand1.less 组件 1 主题色彩变量
| └─brand2.less 组件 2 主题色彩变量
| └─other.less 其余主题色彩变量
└─...
需要:
- 组件目录应用对应的主题色彩变量,其余目录应用主题 3 色彩变量
- 应用主题色彩变量时不须要每次独自 @import ‘@/theme/brandx.less’
解决方案
module: {
rules: [
{
test: /\.less$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", 'less-loader'],
},
{
test: /components\\brand1.*\.less$/i,
use: [{
loader: 'style-resources-loader',
options: {patterns: [path.resolve(__dirname, 'src/theme/brand1.less')]
},
}],
},
{
test: /components\\brand2.*\.less$/i,
use: [{
loader: 'style-resources-loader',
options: {patterns: [path.resolve(__dirname, 'src/theme/brand2.less')]
},
}],
},
{test: /[^components\\].*\.less$/i,
use: [{
loader: 'style-resources-loader',
options: {patterns: [path.resolve(__dirname, 'src/theme/brand3.less')]
},
}],
},
]
},
发散点
loader 匹配规定
loader 会把非 js 的文件转换为 js 文件,不同类型的文件依据定义的 rules 中不同 test 去匹配相应的 loader 进行解决,这里对 vue 组件中款式文件的解决其实是先通过了 vue-loader 将.vue 文件中的 <style lang="less">
局部转换为 less 文件后,再由其余匹配此规定的 loader 进行解决
loader 程序
在之前应用 loader 的过程中,我只晓得 use 中的 loader 程序是从右向左(或者说是从下到上,这个是看书写的习惯),例如上面,执行程序是 less-loader->css-loader->MiniCssExtractPlugin.loader
{
test: /\.less$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", 'less-loader'],
},
其实在 rules 中对于雷同类型文件的匹配程序也是一样;例如咱们把下面的 rules 程序换一下,此时所有的 less 文件在第一个规定中曾经全副转换为 css,无奈匹配第二个规定执行 style-resources-loader,导致其 less 变量并未注入从而引起报错
// error code
module: {
rules: [
{
test: /components\\brand1.*\.less$/i,
use: [{
loader: 'style-resources-loader',
options: {patterns: [path.resolve(__dirname, 'src/theme/brand1.less')]
},
}],
},
{
test: /\.less$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", 'less-loader'],
},
]
}
同时也能够依据 build 信息看到具体的 loader 执行程序
扩大:在很多材料中有介绍 loader 的 pitch 概念
https://webpack.docschina.org/api/loaders/#pitching-loader
这边能够本人写一个例子去感受一下
//src/loaders/a.js
module.exports = function(source) {
// this.data 为在 pitch 阶段和 normal 阶段之间共享的 data 对象。console.log('a-loader',this.data);
return source;
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {console.log('a-pitch**************************');
console.log(remainingRequest);
console.log(precedingRequest);
console.log(data);
data.value = 'a-pitch-value';
}
//src/loaders/b.js
module.exports = function(source) {console.log('b-loader', this.data);
return source;
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {console.log('b-pitch**************************');
console.log(remainingRequest);
console.log(precedingRequest);
console.log(data);
data.value = 'b-pitch-value';
}
//src/loaders/c.js
module.exports = function(source) {console.log('c-loader',this.data);
return source;
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {console.log('c-pitch**************************');
console.log(remainingRequest);
console.log(precedingRequest);
console.log(data);
data.value = 'c-pitch-value';
}
//webpack.config.js
module: {
rules: [
{
use: [path.join(__dirname, 'src/loaders/a.js'),
path.join(__dirname, 'src/loaders/b.js'),
path.join(__dirname, 'src/loaders/c.js'),
],
},
]
},
后果如下:
这个办法如同用的不是很多,目前就在 mini-css-extract-plugin
中看到
调试 loader
而后抉择 node.js
,我的项目外面会多一个.vscode
的文件夹
配置 launch.json
文件
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "webpack",
"program": "D:\\webpack-test\\node_modules\\webpack-cli\\bin\\cli.js",
//"args": [
// "dev"
//]
}
]
}
次要是配置 program
,此为你执行文件的地位,args
为你执行命令时的参数,我这边只是用 webpack 打包,所以不须要此参数,这里等价于让 vscode 自带的 node 调试性能去帮你执行 webpack 这条命令
配置好了之后,就能够间接在 node_modules 外面找到相应的 loader 代码,而后打上断点,按 F5 开始进行调试
style-resources-loader 的 injector 属性
第一次看这个属性的时候可能会看不懂,然而如果会调试了 loader 的话,间接去源码外面去打个断点就晓得怎么用的了
Name | Type | Default | Description |
---|---|---|---|
injector | Function ‘prepend’ ‘append’ |
‘prepend’ | Controls the resources injection precisely |
此属性为 准确管制资源注入
例如:
//a.less
@primary-color: red;
.text-color {color: @primary-color;}
//var.less
@primary-color: black;
当 injector
为prepend
时
//a.css
.text-color {color: red;}
当 injector
为append
时
//a.css
.text-color {color: black;}
从例子能够看进去,管制咱们的变量是否会笼罩原文件中的变量
当 injector
为回调函数时,官网给的例子是
module.exports = {
// ...
module: {
rules: [{
test: /\.styl$/,
use: ['style-loader', 'css-loader', 'stylus-loader', {
loader: 'style-resources-loader',
options: {
patterns: [path.resolve(__dirname, 'path/to/stylus/variables/*.styl'),
path.resolve(__dirname, 'path/to/stylus/mixins/*.styl')
],
injector: (source, resources) => {
const combineAll = type => resources
.filter(({file}) => file.includes(type))
.map(({content}) => content)
.join('');
return combineAll('variables') + combineAll('mixins') + source;
}
}
}]
}]
},
// ...
}
意思就是让咱们本人管制注入的变量内容是来自于哪个文件(variables、mixins)
source 为源文件内容字符串
resources 为注入文件的对象数组,对象蕴含 file 和 content 属性
- file 为该文件的绝对路径
- content 为该文件的文件内容字符串
源码:
const normalizeInjector = (injector: StyleResourcesLoaderOptions['injector']): StyleResourcesNormalizedInjector => {if (typeof injector === 'undefined' || injector === 'prepend') {return (source, resources) => resources.map(getResourceContent).join('') + source;
}
if (injector === 'append') {return (source, resources) => source + resources.map(getResourceContent).join('');
}
return injector;
};