style-loader
支持在options
中設置一些attrs
屬性决侈,這些屬性將被添加到創(chuàng)建的style
標簽上铅协,最終添加到dom樹里
這本功能在開發(fā)調試時是很有用的篷角,比如我們通過他可以知道樣式的源文件與插入的style的對應關系锹引,從而去檢查是否被正確處理了盛卡,以及他們加載的順序是怎樣的
但是當前的style-loader
中定義的attrs
只能是靜態(tài)屬性助隧,不支持像file-loader
定義name
那樣的占位符
翻看style-loader的issues,有人提過這樣的需求滑沧,但是他建議的是把node_modules中的ui組件里的樣式加上組件的id和version, 而我想的是把文件的路徑添加進去
知道想做什么之后并村,就是研究怎么實現了,
翻看file-loader得知使用了loader-utils模塊的 interpolateName 方法
照葫蘆畫瓢
首先給style-loader的attrs設置上帶占位符的屬性
{
loader: 'style-loader',
options:{
attrs:{
path: '[path][name].[ext]'
}
}
}
然后在style-loader中處理
注意style-loader是pitch類Loader, 與普通loader不同滓技,區(qū)別在他們的執(zhí)行順序上
普通loader的順序類似于參數傳參哩牍,比如
use:[
'a-loader',
'b-loader',
'c-loader',
]
其中a,b,c三個Loader都是普通loader,
那么他們的執(zhí)行順序為
a-loader(b-loader(c-loader(request)))
而如果a-loader為pitch類loader, 那么順序可能變成
a-loader.pitch(request);
b-loader(c-loader(request));
也可能變成其他順序,由a-loader.pitch具體實體決定殖属,比如他可以返回一段代碼姐叁,也可以直接攔截掉
在style-loader里,
我們需要把options.attrs的內容用loader-utils.interpolateName方法優(yōu)先處理洗显,再傳給其生成的代碼外潜,也就是使用addStyles
處理,
為什么要優(yōu)先處理挠唆,是因為addStyles接收的是css-loader返回的內容处窥,而css-loader返回的內容不是原始的content, 同時其執(zhí)行的上下文已經不是webpack的環(huán)境,而是瀏覽器玄组,此時是沒有resourcePath等內容的滔驾,所以我們只能在pitch階段把attrs處理好
處理也不難谒麦,下面這段代碼添加到 使用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;
}
效果有了,但是有個問題哆致,多個style標簽的path屬性都一樣绕德,即第一次的那個。
經過一番排查摊阀,找到原因耻蛇,
options是個對象是這么來的
var options = loaderUtils.getOptions(this) || {};
因為我們配置時使用的是options:{attrs:{}} 對象的形式,而不是queryString的方式胞此,getOptions方法直接返回原來的對象臣咖,導致1次修改之后,覆蓋了原來的占位符漱牵,然后后續(xù)的style不正常
解決辦法就是對其deepClone
, 這樣不影響webpack loaders中的配置
var options = {...(loaderUtils.getOptions(this) || {})};
options.attrs = {...(options.attrs||{})}
終于正常
vs