file-loader和url-loader的區別


什么是 url-loader

url-loader 會將引入的文件進行編碼,生成 DataURL,相當於把文件翻譯成了一串字符串,再把這個字符串打包到 JavaScript。

使用 base64 來加載圖片也是有兩面性的:

  • 優點:節省請求,提高頁面性能
  • 缺點:增大本地文件大小,降低加載性能

所以我們得有取舍,只對部分小 size 的圖片進行 base64 編碼,其它的大圖片還是發請求吧。

什么是 file-loader

在css文件中定義background的屬性或者在html中引入image的src,我們知道在webpack打包后這些圖片會打包至定義好的一個文件夾下,和開發時候的相對路徑會不一樣,這就會導致導入圖片路徑的錯誤。而file-loader正是為了解決此類問題而產生的,他修改打包后圖片的儲存路徑,再根據配置修改我們引用的路徑,使之對應引入

之間有什么聯系呢?

url-loader內部封裝了file-loader。url-loader不依賴於file-loader,即使用url-loader時,只需要安裝url-loader即可,不需要安裝file-loader。

通過上面的介紹,我們可以看到,url-loader工作分兩種情況:

  1. 文件大小小於limit參數,url-loader將會把文件轉為DataURL;
  2. 文件大小大於limit,url-loader會調用file-loader進行處理,參數也會直接傳給file-loader。因此我們只需要安裝url-loader即可

手寫實現file-loader

file-loader 的工作流程如下:

  • 通過 loaderUtils.interpolateName 方法可以根據 options.name 以及文件內容生成一個唯一的文件名 url(一般配置都會帶上hash,否則很可能由於文件重名而沖突)

  • 通過 this.emitFile(url, content) 告訴 webpack 我需要創建一個文件,webpack會根據參數創建對應的文件,放在 public path 目錄下。

  • 返回 'module.exports = webpack_public_path + '+ JSON.stringify(url) + ‘;’ ,這樣就會把原來的文件路徑替換為編譯后的路徑

對file-loader 中最重要的幾行代碼解釋如下(我們自己來實現一個 file-loader 就只需要這幾行代碼就行了,完全可以正常運行且支持 name 配置):

var loaderUtils = require('loader-utils')

module.exports = function (content) {

  // 獲取options,就是 webpack 中對 file-loader 的配置,比如這里我們配置的是 `name=[name]_[hash].[ext]`
  // 獲取到的就是這樣一個k-v 對象 { name: "[name]_[hash].[ext]" }
  const options = loaderUtils.getOptions(this) || {};

  // 這是 loaderUtils 的一個方法,可以根據 name 配置和 content 內容 生成一個文件名。為什么需要 文件內容呢?這是為了保證當文件內容沒有發生變化的時候,名字中的 [hash] 字段也不會變。可以理解為用文件的內容作了一個hash
  let url = loaderUtils.interpolateName(this, options.name, {
    content
  })

  this.emitFile(url, content) // 告訴webpack,我要創建一個文件,文件名和內容,這樣webpack就會幫你在 dist 目錄下創建一個對應的文件

  // 這里要用到一個變量,就是 __webpack_public_path__ ,這是一個由webpack提供的全局變量,是public的根路徑
  // 參見:https://webpack.js.org/guides/public-path/#on-the-fly
  // 這里要注意一點:這個返回的字符串是一段JS,顯然,他是在瀏覽器中運行的
  // 舉個栗子:
  // css源碼這樣寫:background-image: url('a.png')
  // 編譯后變成: background-image: require('xxxxxx.png')
  // 這里的 require 語句返回的結果,就是下面的 exports 的字符串,也就是圖片的路徑
  return 'module.exports = __webpack_public_path__ + '+ JSON.stringify(url)
}

// 一定別忘了這個,因為默認情況下 webpack 會把文件內容當做UTF8字符串處理,而我們的文件是二進制的,當做UTF8會導致圖片格式錯誤。
// 因此我們需要指定webpack用 raw-loader 來加載文件的內容,而不是當做 UTF8 字符串傳給我們
module.exports.raw = true

手寫url-loader

url-loader 的工作流程如下:

  • 獲取 limit 參數
  • 如果 文件大小在 limit 之類,則直接返回文件的 base64 編碼后內容
  • 如果超過了 limit ,則調用 file-loader
  • 因為邏輯比較簡單,這里直接放上源碼以及我添加的注釋:
module.exports = function(content) {

    // 獲取 options 配置,上面已經講過了就不在重復
  var options =  loaderUtils.getOptions(this) || {};
  // Options `dataUrlLimit` is backward compatibility with first loader versions
    // limit 參數,只有文件大小小於這個數值的時候我們才進行base64編碼,否則將直接調用 file-loader
  var limit = options.limit || (this.options && this.options.url && this.options.url.dataUrlLimit);

  if(limit) {
    limit = parseInt(limit, 10);
  }

  var mimetype = options.mimetype || options.minetype || mime.lookup(this.resourcePath);

  // No limits or limit more than content length
  if(!limit || content.length < limit) {
    if(typeof content === "string") {
      content = new Buffer(content);
    }

        // 直接返回 base64 編碼的內容
    return "module.exports = " + JSON.stringify("data:" + (mimetype ? mimetype + ";" : "") + "base64," + content.toString("base64"));
  }

    // 超過了文件大小限制,那么我們將直接調用 file-loader 來加載
  var fallback = options.fallback || "file-loader";
  var fallbackLoader = require(fallback);

  return fallbackLoader.call(this, content);
}



// 一定別忘了這個,因為默認情況下 webpack 會把文件內容當做UTF8字符串處理,而我們的文件是二進制的,當做UTF8會導致圖片格式錯誤。
// 因此我們需要指定webpack用 raw-loader 來加載文件的內容,而不是當做 UTF8 字符串傳給我們
// 參見:https://webpack.github.io/docs/loaders.html#raw-loader
module.exports.raw = true

總結

  • file-loader 返回的是文件的路徑
  • url-loader 返回的是文件的base64編碼

參考文獻

  • 詳解webpack url-loader和file-loader:https://segmentfault.com/a/1190000018987483
  • webpack 源碼解析:file-loader 和 url-loader:https://www.cnblogs.com/shiyunfront/articles/8944940.html

 

歡迎關注前端早茶,與廣東靚仔攜手共同進階

前端早茶專注前端,一起結伴同行,緊跟業界發展步伐~

公眾號作者:廣東靚仔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM