file-loader 配置詳解以及資源相對路徑處理

vue-loader工作流程梳理 里我們提到,vue-loader 編譯的一環(huán)中(樣式部分會應用到 css-loader )<style><template>中引用的資源會被轉換成模塊請求赃承,即require('xxx.png')的形式域帐。而 file-loader 則會將資源文件復制到指定的打包目錄赘被,同時把原本的模塊引用(import/require())解析重寫為輸出文件的正確訪問路徑(url)。

資源文件輸出路徑/訪問路徑

簡單來說肖揣,file-loader主要解決兩件事:
1 指定輸出文件的路徑——即打包后文件的存儲位置民假。
2 生成解析文件的路徑——即打包后引用文件時的URL地址。

開發(fā)階段在css或html標簽中引用的資源路徑龙优,通常和項目打包后資源的訪問路徑不一樣羊异。因此在配置 file-loader 的過程中我們要把握和厘清輸出目錄outputPath和引用路徑前綴publicPath這兩項(可以按照 webpack 的output.pathoutput.publicPath的機制來理解)彤断,不然可能導致項目運行時圖片報404錯誤球化。

vue-cli4 的默認配置下,圖片文件都會被輸出在/dist/static/img目錄瓦糟,同時引用地址會被解析成絕對路徑重寫入url中筒愚。像這樣:background-image: url(/static/img/denglun-bg.ba926c29.jpg)。但是絕對路徑不夠靈活菩浙,比如用 nginx 配置 HTTPS 服務 時將項目部署在二級目錄下巢掺,直接訪問根目錄肯定會出錯。
開發(fā)環(huán)境用絕對路徑不會有問題劲蜻,而生產(chǎn)環(huán)境最終通過 mini-css-extract-plugin 把每個.vue內的樣式都提取到單獨的.css文件中陆淀。然后通過正確配置插件的publicPath,或者 file-loader 的 publicPath將 url 寫成相對路徑先嬉。

注??:一般測試環(huán)境打包(vue-cli-service serve)也會輸出打包文件到我們配置的dist目錄轧苫,只是都在內存中不可見罷了

file-loader 的配置項詳解,即傳遞給 options 的參數(shù)疫蔓。

  • outputPath 資源打包輸出時存放的目錄(相對于打包目錄的路徑)
    默認值為 undefined含懊,即直接輸出在 dist (默認的打包目錄) 下

    最終導出的文件路徑webpackConfig.output.path + file-loader.outputPath + file-loader.name
    若 file-loader 的配置為{ outputPath: 'static/img', file-loader.name: '[name].[contenthash].[ext]' }時,(static是我的靜態(tài)資源目錄)衅胀,最后打包圖片存放的路徑將會是dist/static/img/logo.da7ef7de.png

  • publicPath 定義目標文件的公共訪問路徑(前綴)岔乔,即項目運行時能正確引用資源的路徑前綴
    默認值為 webpackConfig.output.publicPath + file-loader.outputPath
    最終引用的文件路徑則為 webpackConfig.output.publicPath + file-loader.outputPath + file-loader.name 滚躯。項目運行時我們訪問的index.html的根目錄就是 dist雏门,因此訪問路徑會像這樣:/static/img/logo.da7ef7de.png嘿歌。

  • name:打包后輸出的文件名
    我們可以把 outputPath 的內容直接寫到name中,即在前面加上存放目錄茁影,一樣可以生成需要的輸出路徑宙帝。這也是vue-cli4默認的做法。如:

    .loader('file-loader')
    .options({
      name: '[name].[contenthash].[ext]',
      outputPath: 'static/img'
    })
    

    簡化成

    .loader('file-loader')
    .options({
      name: 'static/img/[name].[contenthash].[ext]'
    })
    

從生成的資源覆寫 filename 或 chunkFilename 時募闲,vue.config.js配置的assetsDir 會被忽略步脓。
因此別忘了在前面加上靜態(tài)資源目錄,即assetsDir指定的目錄蝇更,不然會直接在dist文件夾下,配置 outputPath 時同理呼盆。

用函數(shù)作為outputPath/publicPath選項的值

我們可以通過配置 publicPath 項使資源引用 URL 為相對路徑年扩,簡易版:

.loader('file-loader')
.options: {
  name: '[name].[contenthash].[ext]', 
  outputPath: 'static/img',
  publicPath: '../static/img'
  // publicPath: 'static/img' // 也可
}

另外,如要給不同資源分別定義存儲目錄/訪問路徑前綴访圃,可以用函數(shù)來配置。outputPath/publicPath 的回調參數(shù)如下:

  • url 是 options.name 選項的值腿时,如'[name].[contenthash].[ext]'况脆,必須配置在路徑最后批糟。需要注意當自定義配置了 outputPath 就不要在 options.name 里再加上目錄了盛末,name 只負責文件名就好,不然這樣得到的 url 參數(shù)有了目錄前綴檐嚣,就不太方便再處理。
  • resourcePath 是資源打包前的原始絕對路徑
  • context 是資源文件的根部上下文(rootContext)隐解,也就是項目根目錄的絕對路徑挖藏,或你自定義的 context 配置項

可以這樣獲取項目根目錄到資源的相對路徑:
const relativePath = path.relative(context, resourcePath);

path.relative(from, to) 方法:返回從fromto的相對路徑
舉例:
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');,會返回:'../../impl/bbb'
path.relative('/', '/src/assets/bg_images/main-bg.jpg');厢漩,會返回'src/assets/bg_images/main-bg.jpg'

//  outputPath 和 publicPath 配置演示膜眠,非vue-cli4的處理:
.options({
  name: '[name].[contenthash].[ext]',
  // outputPath: 'static/img', // 別忘了加上靜態(tài)資源目錄這個前綴,即assetsDir指定的目錄,不然會直接在dist文件夾下
  outputPath: function (url, resourcePath, context) {
    // 返回從項目根目錄到該圖片的相對路徑
    const relativePath = path.relative(context, resourcePath)
    const pathArr = relativePath.split('/')
    // 如果你的靜態(tài)資源目錄結構較為簡單(最多二個層級)宵膨,圖片只放在/src/assets/ 或/src/assets/xxx
    // 希望根據(jù)assets下的目錄結構原樣輸出架谎,可以這樣做
    if (pathArr[3] !== undefined) {
      return `static/img/${pathArr[2]}/${url}` // url 是上面配置的 name 的值,必須加在路徑最后
    }
    return `static/img/${url}`

    // 這些都可依照個人習慣來安排辟躏,個人建議沒必要太復雜
    // if (/denglun-bg\.jpg/.test(resourcePath)) {
      // 如果圖片以 denglun-bg.jpg 結尾
      // return `static/denglun/${url}`
    // }
    // if (/bg_images\//.test(resourcePath)) {
      // 如果圖片路徑包含 bg_images 目錄
      // return `static/bg_images/${url}`
    // }
    // return `static/img/${url}`
  },
  publicPath: (url, resourcePath, context) => {
    // 如果要讓資源引用地址輸出為相對路徑谷扣,把 `outputPath` 的內容拷貝一份到這里即可
  }
},

這種情況如果需要指定資源引用URL為相對路徑,也需用函數(shù)配置 publicPath捎琐,照著 outputPath 的內容依葫蘆畫瓢即可会涎。

先看看vue-cli4生產(chǎn)環(huán)境打包的默認處理結果:

我們打包前的靜態(tài)資源目錄(assets)的結構如下(只展示圖片路徑相關資源):

src
├─ assets
│    ├─ bg_images
│    │    └─ denglun-bg.jpg
│    ├─ denglun-bg.jpg
│    ├─ denglun-limit.jpg
│    └─ denglun.jpg
├─ styles
│    └─ index.scss
└─ views
       └─ dashboard
              └─ index.vue

打包目錄的結構(同樣只是做個演示):

dist
├─ index.html
└─ static
     ├─ css
     │    ├─ app.02c07ea3.css
     │    ├─ chunk-01c6456a.1d1f9d97.css
     │    ├─ chunk-components.4732cb5c.css
     │    └─ chunk-libs.a1d59a71.css
     ├─ img
     │    ├─ denglun-bg.ba926c29.jpg
     │    ├─ denglun-limit.433994b0.jpg
     │    └─ denglun.901f400f.jpg
     └─ js
         ├─ app.60f57078.js
         └─ chunk-libs.8726a2b2.js
打包前的src/views/dashboard/index.vue
打包前的src/styles/index.scss
打包后圖片的引用路徑

原先打包自認為是小圖片卻沒轉成內聯(lián)base64?原來是其實我的圖片全部都大于默認的limit值(也就是4096/4kb瑞凑,不夠仔細)末秃,改成10240后效果才出來:

完美~
如果我們在 file-loader 配置outputPath使圖片輸出在不同的目錄

測試環(huán)境打包后,css中的資源URL默認是/static/img/xxx.h86a0sh3.jpg這樣的絕對路徑∽延現(xiàn)在我們來看看生產(chǎn)環(huán)境輸出的*.css文件中引用的資源相對路徑是怎么處理的练慕。

用 mini-css-extract-plugin 打包 css 時資源URL路徑配置

這件事 vue-cli4 通過 mini-css-extract-plugin@0.9.0 已經(jīng)幫我們完美處理好了~ ??注意,這個插件最新版本把publicPath屬性放到loader下了技掏,chainWebpack 鏈式配置時要放到 loaderOptions 里铃将。?? 如果手動安裝配置mini-css-extract-plugin的話要留意區(qū)分。

打包后css文件的輸出路徑是dist/靜態(tài)資源目錄/css/name.[contenthash:8].css哑梳,如:dist/static/css/app.e3db5d0a.css劲阎,源碼:

// ./node_modules/@vue/cli-service/lib/config/css.js
const filename = getAssetPath(
  rootOptions,
  `css/[name]${rootOptions.filenameHashing ? '.[contenthash:8]' : ''}.css`
)

由此css文件的訪問絕對路徑是/靜態(tài)資源目錄/css/文件名.css,如:/static/css/app.e3db5d0a.css

生產(chǎn)環(huán)境模式鸠真,vue-cli 4 做了如下配置:

// ./node_modules/@vue/cli-service/lib/config/css.js
module.exports = (api, rootOptions) => {
  api.chainWebpack(webpackConfig => {
    // use relative publicPath in extracted CSS based on extract location
    // 設置 publicPath 為輸出的 css 文件基于項目打包根目錄的相對路徑
    const cssPublicPath = process.env.VUE_CLI_BUILD_TARGET === 'lib'
      // 在 lib 模式下, CSS 會被提取到根目錄下
      ? './'
      : '../'.repeat( // 將filename路徑最前面的 './' '.\'先去掉哪工,如果是'/'(絕對路徑)就原樣輸出,再根據(jù) (/ 或 \ 的 數(shù)量) -1弧哎,確定重復 '../' 的從次數(shù)
        extractOptions.filename // 這個filename就是css文件的輸出文件名
            .replace(/^\.[\/\\]/, '')
            .split(/[\/\\]/g)
            .length - 1
      )
    function createCSSRule (lang, test, loader, options) {
      // ... 省略了大段代碼雁比,主要截取配置publicPath部分,具體可以去源碼
      function applyLoaders (rule, isCssModule) {
        if (shouldExtract) { // 若shouldExtract為true撤嫩,表示生產(chǎn)環(huán)境且非shadowMode
          rule
            .use('extract-css-loader')
              .loader(require('mini-css-extract-plugin').loader)
              .options({
                hmr: !isProd,
                publicPath: cssPublicPath // 默認值是 webpack 配置的 output.publicPath
              })
        }
        // ...省略
      }
    }  
  }
}

mini-css-extract-plugin 的作用是為每個包含css的js文件創(chuàng)建一個單獨的.css文件偎捎。

我們重點關注一下它 Loader 選項的 publicPath<String|Function> 為 css 內引入的圖片、文件等外部資源指定一個URL公共路徑序攘。
默認值為vue.config.js的 publicPath (也就是 webpack 的 output.publicPath茴她,一般是'/')。css中引入的URL最終會處理成這個publicPath+ 該資源文件的訪問路徑程奠。

通過分析代碼中的變量 cssPublicPath 得出丈牢,它的值即為打包后的 css 文件基于 dist 的相對路徑。用這個前綴再加上資源的訪問路徑即可高枕無憂了(never goes wrong)瞄沙。最終就是像url(../../static/img/denglun-bg.4baebe12.jpg)

看下 mini-css-extract-plugin 用函數(shù)配置 publicPath 的例子己沛,也是一樣的效果:

options: {
  publicPath: (resourcePath, context) => {
    // publicPath 是css文件相對于上下文(項目根目錄)的相對路徑
    // path.dirname(resourcePath) 慌核,返回resourcePath的目錄
    return path.relative(path.dirname(resourcePath), context) + '/';
  },
},

其中:path.dirname(path) 的返回值是當前路徑的上層目錄(絕對路徑)
例如:/css/main.css 所在目錄為/css,那么相對于項目根目錄的路徑就是 ../申尼;
/static/css/index.css所在目錄是/static/css垮卓,publicPath 就會是 ../../

當我們用 image-webpack-loader 壓縮圖片后,size會小很多师幕,基本都會被轉成base64 URI內聯(lián)在css和js中粟按。后面這些就不用 mini-css-extract-plugin 處理css中資源的相對路徑了。反正目標是靜態(tài)資源小一點再小一點霹粥,需要 file-loader 輸出的資源越少越好灭将。

and file-loaderurl-loader 在 webpack5 就棄用了??,被資源模塊(asset module)取代以后不用配置 loader 了后控,可以去了解下庙曙。

最后(也是寫給自己):碰到不清楚的地方,一定要多看官方文檔多分析源碼(細讀官網(wǎng)避免亂百度的彎路忆蚀,碰到不懂的就去看源碼理解透徹)矾利,靠自己厘清實現(xiàn)原理姑裂。不要框架幫忙整合好了就不管不顧了馋袜。不然永遠只是照搬別人的配置,版本升級或者換個插件就不會了舶斧,毫無收獲欣鳖。
今后努力方向:精煉明了,杜絕長篇大論茴厉。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末泽台,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子矾缓,更是在濱河造成了極大的恐慌怀酷,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗜闻,死亡現(xiàn)場離奇詭異蜕依,居然都是意外死亡,警方通過查閱死者的電腦和手機琉雳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門样眠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人翠肘,你說我怎么就攤上這事檐束。” “怎么了束倍?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵被丧,是天一觀的道長盟戏。 經(jīng)常有香客問我,道長晚碾,這世上最難降的妖魔是什么抓半? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮格嘁,結果婚禮上笛求,老公的妹妹穿的比我還像新娘。我一直安慰自己糕簿,他們只是感情好探入,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著懂诗,像睡著了一般蜂嗽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上殃恒,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天植旧,我揣著相機與錄音,去河邊找鬼离唐。 笑死病附,一個胖子當著我的面吹牛,可吹牛的內容都是我干的亥鬓。 我是一名探鬼主播完沪,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嵌戈!你這毒婦竟也來了覆积?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤熟呛,失蹤者是張志新(化名)和其女友劉穎宽档,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庵朝,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡吗冤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了偿短。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欣孤。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昔逗,靈堂內的尸體忽然破棺而出降传,到底是詐尸還是另有隱情,我是刑警寧澤勾怒,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布婆排,位于F島的核電站声旺,受9級特大地震影響,放射性物質發(fā)生泄漏段只。R本人自食惡果不足惜腮猖,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赞枕。 院中可真熱鬧澈缺,春花似錦、人聲如沸炕婶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柠掂。三九已至项滑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涯贞,已是汗流浹背枪狂。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宋渔,地道東北人州疾。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像傻谁,于是被迫代替她去往敵國和親孝治。 傳聞我的和親對象是個殘疾皇子列粪,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容