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的话,间接去源码外面去打个断点就晓得怎么用的了

NameTypeDefaultDescription
injectorFunction
'prepend'
'append'
'prepend'Controls the resources injection precisely

此属性为准确管制资源注入

例如:

//a.less@primary-color: red;.text-color {    color: @primary-color;}//var.less@primary-color: black;

injectorprepend

//a.css.text-color {  color: red;}

injectorappend

//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;};