自定义webpack loader主要用于处理文件内容,比如解析自定义格式的文件(最常见的如解析.vue文件或jsx文件),或者对文件内容进行替换。
案例:解析自定义格式的样式文件
这里假设有一个文件:index.uss,格式为yaml,期望通过这个文件生成原子级样式, 文件内容如下:
space:
unit:
px: 0, 4, 8, 16, 24, 48, 120, 240
full: 100%
fontSize:
px: 12, 14, 16, 18, 24, 36, 48
base: 14
预期能解析为
.m-0 { margin: 0px; }
.p-0 { padding: 0px; }
.m-4 { margin: 4px; }
.p-4 { padding: 4px; }
.f-48 { font-size: 48px; }
.f-base { font-size: 14px; }
...
这种自定义格式的文件的解析需求,非常符合loader的使用场景。
loader实现
我们可以在项目中定义loader如下:
const YAML = require('yaml')
module.exports = function (source) {
const config = YAML.parse(source)
const spaceUnit = config.space.unit.px.split(',');
const full = config.space.unit.full;
const fontSize = config.fontSize.px.split(',');
const fontBase = config.fontSize.base;
let content = '';
let spaceItem = [['m','margin'],['p','padding']];
for(let i=0; i<spaceUnit.length; i++){
let unit = spaceUnit[i];
spaceItem.forEach(([sort,name])=>{
content+=`.${sort}-${unit.trim()} { ${name}: ${unit}px; } \n`
})
}
spaceItem.forEach(([sort,name])=>{
content+=`.${sort}-full { ${name}: ${full}; } \n`
})
fontSize.forEach((size,index)=>{
content+=`.f-${size.trim()} { font-size: ${size}px; } \n`
})
content+=`.f-base { font-size: ${fontBase}px; } \n`;
return content;
}
更改webapck配置
该文件被自定义的loader处理完后,需要交给css-loader和style-loader进行后续处理:
module: {
rules: [{
test: /\.uss$/i,
use: ['style-loader', 'css-loader', path.resolve('./ucss-loader.js')]
},
...
},
这里有个需要注意的是,如果是项目本地的loader,需要通过path.resolve引入(当然也可以设置resolveLoader的配置项,但并不如这种方式直接)
最终效果
最终如预期一样,生成了原子样式:
loader的其他常用api
可以在loader的fucntion中通过this访问其他api
this.async
返回一个callback,在loader中处理异步任务
import path from 'path';
export default function (source) {
var callback = this.async();
var headerPath = path.resolve('header.js');
this.addDependency(headerPath);
fs.readFile(headerPath, 'utf-8', function (err, header) {
if (err) return callback(err);
callback(null, header + '\n' + source);
});
}
this.getOptions
如果loader需要传入配置参数,可以用该api获取传入项
export default function loader(source) {
const options = this.getOptions();
source = source.replace(/[name]/g, options.name);
return `export default ${JSON.stringify(source)}`;
}