style-loader支持在options中設(shè)置一些attrs屬性,這些屬性將被添加到創(chuàng)建的style標(biāo)簽上,最終添加到dom樹(shù)里
這本功能在開(kāi)發(fā)調(diào)試時(shí)是很有用的,比如我們通過(guò)他可以知道樣式的源文件與插入的style的對(duì)應(yīng)關(guān)系,從而去檢查是否被正確處理了,以及他們加載的順序是怎樣的
但是當(dāng)前的style-loader中定義的attrs只能是靜態(tài)屬性,不支持像file-loader定義name那樣的占位符
翻看style-loader的issues,有人提過(guò)這樣的需求,但是他建議的是把node_modules中的ui組件里的樣式加上組件的id和version, 而我想的是把文件的路徑添加進(jìn)去
知道想做什么之后,就是研究怎么實(shí)現(xiàn)了,
翻看file-loader得知使用了loader-utils模塊的 interpolateName 方法
照葫蘆畫瓢
首先給style-loader的attrs設(shè)置上帶占位符的屬性
{
loader: 'style-loader',
options:{
attrs:{
path: '[path][name].[ext]'
}
}
}
然后在style-loader中處理
注意style-loader是pitch類Loader, 與普通loader不同,區(qū)別在他們的執(zhí)行順序上
普通loader的順序類似于參數(shù)傳參,比如
use:[
'a-loader',
'b-loader',
'c-loader',
]
其中a,b,c三個(gè)Loader都是普通loader,
那么他們的執(zhí)行順序?yàn)?br>
a-loader(b-loader(c-loader(request)))
而如果a-loader為pitch類loader, 那么順序可能變成
a-loader.pitch(request);
b-loader(c-loader(request));
也可能變成其他順序,由a-loader.pitch具體實(shí)體決定,比如他可以返回一段代碼,也可以直接攔截掉
在style-loader里,
我們需要把options.attrs的內(nèi)容用loader-utils.interpolateName方法優(yōu)先處理,再傳給其生成的代碼,也就是使用addStyles處理,
為什么要優(yōu)先處理,是因?yàn)閍ddStyles接收的是css-loader返回的內(nèi)容,而css-loader返回的內(nèi)容不是原始的content, 同時(shí)其執(zhí)行的上下文已經(jīng)不是webpack的環(huán)境,而是瀏覽器,此時(shí)是沒(méi)有resourcePath等內(nèi)容的,所以我們只能在pitch階段把a(bǔ)ttrs處理好
處理也不難,下面這段代碼添加到 使用JSON.stringify(options)之前
const context = options.context || this.rootContext || (this.options && this.options.context);
for(let key in options.attrs||{}){
let name = loaderUtils.interpolateName(this,options.attrs[key],{
context,
content:null,
regExp:options.regExp,
});
options.attrs[key]=name;
}
效果有了,但是有個(gè)問(wèn)題,多個(gè)style標(biāo)簽的path屬性都一樣,即第一次的那個(gè)。
經(jīng)過(guò)一番排查,找到原因,
options是個(gè)對(duì)象是這么來(lái)的
var options = loaderUtils.getOptions(this) || {};
因?yàn)槲覀兣渲脮r(shí)使用的是options:{attrs:{}} 對(duì)象的形式,而不是queryString的方式,getOptions方法直接返回原來(lái)的對(duì)象,導(dǎo)致1次修改之后,覆蓋了原來(lái)的占位符,然后后續(xù)的style不正常
解決辦法就是對(duì)其deepClone, 這樣不影響webpack loaders中的配置
var options = {...(loaderUtils.getOptions(this) || {})};
options.attrs = {...(options.attrs||{})}
終于正常

vs
