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 codemodule: { 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.jsmodule.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.jsmodule.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.jsmodule.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.jsmodule: { 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;};