炒冷飯系列 -- 10 分鐘搞定 url-loader

原文地址javascript

什麼是 url-loader

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

何時使用

通常來講,咱們會發請求來獲取圖片或者字體文件。若是圖片文件較多時(好比一些 icon),會頻繁發送請求來回請求屢次,這是沒有必要的。此時,咱們能夠考慮將這些較小的圖片放在本地,而後使用 url-loader 將這些圖片經過 base64 的方式引入代碼中。這樣就節省了請求次數,從而提升頁面性能。webpack

如何使用

1. 安裝 url-loader

npm install url-loader --save-dev

2. 配置 webapck

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {},
          },
        ],
      },
    ],
  },
};

3. 引入一個文件,能夠是 import(或 require

import logo from '../assets/image/logo.png';
console.log('logo的值: ', logo); // 打印一下看看 logo 是什麼

簡單三步就搞定了。git

4. 見證奇蹟的時刻

webpack

執行 webpack 以後,dist 目錄只生成了一個 bundle.js。和 file-loader 不一樣的是,沒有生成咱們引入的那個圖片。上文說過,url-loader 是將圖片轉換成一個 DataURL,而後打包到 JavaScript 代碼中。github

那咱們就看看 bundle.js 是否有咱們須要的 DataURLweb

// bundle.js
(function(module, exports) {
module.exports = "data:image/jpeg;base64.........."; // 省略無數行
})

咱們能夠看到這個模塊導出的是一個標準的 DataURLnpm

一個標準的DataURL: data:[<mediatype>][;base64],<data>

經過這個 DataURL,咱們就能夠從本地加載這張圖片了,也就不用將圖片文件打包到 dist 目錄下。json

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

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

因此咱們得有取捨,只對部分小 size 的圖片進行 base64 編碼,其它的大圖片仍是發請求吧。工具

url-loader 天然是已經作了這個事情,咱們只要經過簡單配置便可實現上述需求。

options

  • limit: 文件閾值,當文件大小大於 limit 的時候使用 fallbackloader 來處理文件
  • fallback: 指定一個 loader 來處理大於 limit 的文件,默認值是 file-loader

咱們來試試設一個 limit

{
  test: /\.(png|jpg|gif)$/,
  use: [
    {
      loader: 'url-loader',
      options: {
        limit: 1000, // 大於 1000 bytes 的文件都走 fallback
      },
    },
  ],
},

從新執行 webpack,因爲咱們引入的 logo.png 大於 1000,因此使用的是 file-loader 來處理這個文件。圖片被打包到 dist 目錄下,而且返回的值是它的地址:

(function(module, exports, __webpack_require__) {
module.exports = __webpack_require__.p + "dab1fd6b179f2dd87254d6e0f9f8efab.png";
}),

更多關於 file-loader

源碼解析

file-loader 的代碼也很少,就直接複製過來經過註釋講解了:

import { getOptions } from 'loader-utils'; // loader 工具包
import validateOptions from 'schema-utils'; // schema 工具包
import mime from 'mime';

import normalizeFallback from './utils/normalizeFallback'; // fallback loader
import schema from './options.json'; // options schema

// 定義一個是否轉換的函數
/*
 *@method shouldTransform
 *@param {Number|Boolean|String} limit 文件大小閾值
 *@param {Number} size 文件實際大小
 *@return {Boolean} 是否須要轉換
*/
function shouldTransform(limit, size) {
  if (typeof limit === 'boolean') {
    return limit;
  }

  if (typeof limit === 'number' || typeof limit === 'string') {
    return size <= parseInt(limit, 10);
  }

  return true;
}

export default function loader(src) {
  // 獲取 webpack 配置裏的 options
  const options = getOptions(this) || {};

  // 校驗 options
  validateOptions(schema, options, {
    name: 'URL Loader',
    baseDataPath: 'options',
  });

  // 判斷是否要轉換,若是要就進入,不要就往下走
  // src 是一個 Buffer,因此能夠經過 src.length 獲取大小
  if (shouldTransform(options.limit, src.length)) {
    const file = this.resourcePath;
    // 獲取文件MIME類型,默認值是從文件取,好比 "image/jpeg"
    const mimetype = options.mimetype || mime.getType(file);

    // 若是 src 不是 Buffer,就變成 Buffer
    if (typeof src === 'string') {
      src = Buffer.from(src);
    }
    
    // 構造 DataURL 並導出
    return `module.exports = ${JSON.stringify(
      `data:${mimetype || ''};base64,${src.toString('base64')}`
    )}`;
  }

  // 判斷結果是不須要經過 url-loader 轉換成 DataURL,則使用 fallback 的 loader
  const {
    loader: fallbackLoader,
    options: fallbackOptions,
  } = normalizeFallback(options.fallback, options);

  // 引入 fallback loader
  const fallback = require(fallbackLoader);

  // fallback loader 執行環境
  const fallbackLoaderContext = Object.assign({}, this, {
    query: fallbackOptions,
  });

  // 執行 fallback loader 來處理 src
  return fallback.call(fallbackLoaderContext, src);
}

// 默認狀況下 webpack 對文件進行 UTF8 編碼,當 loader 須要處理二進制數據的時候,須要設置 raw 爲 true
export const raw = true;

參考

相關文章
相關標籤/搜索