Webpack loader 之 url-loader

简介

安装

npm install --save-dev url-loader

用法

url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。

import img from './webpack-logo.png'

webpack.config.js

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

常用配置项

limit

limit 用于配置需内联的文件字节限制,类型是 Number,默认值为 undefined。如果文件大于限制(以字节为单位),该文件将交由 file-loader 处理 ,并将所有查询参数传递给它。

webpack.config.js

{
  loader: 'url-loader',
  options: {
    limit: 8192
  }
}

mimetype

mimetype 用于设置文件的 MIME 类型。如果未指定,则将使用文件扩展名来查找对应的 MIME 类型。

webpack.config.js

{
  loader: 'url-loader',
  options: {
    mimetype: 'image/png'
  }
}

fallback

fallback 用于设置当 url-loader 加载的文件大于限制时,所对应的处理器。类型是 String,默认值是 “file-loader”。

webpack.config.js

{
  loader: 'url-loader',
  options: {
    fallback: 'responsive-loader'
  }
}

loader 准则

编写 loader 时应该遵循以下准则:

  • 简单易用
  • 使用链式传递。
  • 模块化的输出。
  • 确保无状态
  • 使用 loader utilities
  • 记录 loader 的依赖
  • 解析模块依赖关系
  • 提取通用代码
  • 避免绝对路径
  • 使用 peer dependencies

以上的准则按重要程度排序,但某些仅适用于某些场景。若想进一步了解自定义 loader,可以阅读 编写一个 loader 这个文档。接下来,我们来基于上述的准则分析一下 url-loader 的源码。

url-loader 源码简析

所谓 loader 只是一个导出为函数对象的 JavaScript 模块。loader runner 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件传入进去。函数的 this 上下文将由 webpack 填充,并且 loader runner 具有一些有用方法,可以使 loader 改变为异步调用方式,或者获取 query 参数

其实本文介绍的 url-loader 在处理 limit 范围内文件时,并不是复制文件,而是把文件转成 Data URLs,然后直接内嵌到 html/css/js 文件中。

导入依赖项

import { getOptions } from 'loader-utils';
import validateOptions from 'schema-utils';
import mime from 'mime';

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

// Loader 模式 -> module.exports.raw = loader.raw;
export const raw = true;

需要注意的是,默认情况下 webpack 会对文件内容进行 UTF8 编码,当 loader 需要处理二进制数据时,需要设置 raw 为 true。此后,webpack 用使用 raw-loader 来加载所指定的文件,而不会对文件内容进行 UTF8 编码。

获取配置对象及验证

export default function loader(src) {
  // Loader Options
  const options = getOptions(this) || {};

  validateOptions(schema, options, 'URL Loader');
}

url-loader 会先调用 loaderUtils 对象的 getOptions 方法,获取当前 loader 对应的配置对象,然后基于已定义的 Schema,验证配置对象的有效性。对应的 Schema 定义如下:

{
  "type": "object",
  "properties": {
    "limit": {
      "type": ["string", "number"]
    },
    "mimetype": {
      "type": "string"
    },
    "fallback": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "additionalProperties": false,
          "properties": {
            "loader": {
              "description": "Fallback loader name",
              "type": "string"
            },
            "options": {
              "description": "Fallback loader options",
              "anyOf": [
                {
                  "type": "object"
                },
                {
                  "type": "string"
                }
              ]
            }
          },
          "type": "object"
        }
      ]
    }
  },
  "additionalProperties": true
}

处理 limit

const file = this.resourcePath; // 资源文件路径
  // 获取文件的大小限制
  let limit = options.limit; 

  if (limit) { // 解析为数值型
    limit = parseInt(limit, 10);
  }
  // 获取文件MIME类型
  const mimetype = options.mimetype || mime.getType(file);

  // No limit or within the specified limit
  if (!limit || src.length < limit) {
    if (typeof src === 'string') {
      src = Buffer.from(src);
    }

    // 返回Data URLs格式 
    return `module.exports = ${JSON.stringify(
      `data:${mimetype || ''};base64,${src.toString('base64')}`
    )}`;
  }

Data URLs 由四个部分组成:前缀(data:)、指示数据类型的MIME类型、如果非文本则为可选的base64标记、数据本身:

data:[<mediatype>][;base64],<data>

mediatype 是个 MIME 类型的字符串,例如 “image/jpeg“ 表示 JPEG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII

如果数据是文本类型,你可以直接将文本嵌入 (根据文档类型,使用合适的实体字符或转义字符)。如果是二进制数据,你可以将数据进行 base64 编码之后再进行嵌入。

处理 fallback

// 规范化fallback参数,处理字符串或对象的形式
// 当options.fallback为字符串时,还会调用loaderUtils
// parseQuery方法解析查询参数
const {
    loader: fallbackLoader,
    options: fallbackOptions,
} = normalizeFallback(options.fallback, options);

// 获取fallback对应的loader
const fallback = require(fallbackLoader);

// Call the fallback, passing a copy of the loader context. The copy has the query replaced. This way, the fallback
// loader receives the query which was intended for it instead of the query which was intended for url-loader.
const fallbackLoaderContext = Object.assign(
  {}, 
  this, 
  {
    query: fallbackOptions,
});

return fallback.call(fallbackLoaderContext, src);

总结

Webpack loader 之 file-loader 文章中,我们简单介绍了 file-loader。那么 url-loader 和 file-loader 之间有什么关系呢?通过阅读 url-loader 源码,我们知道 url-loader 有两种工作模式:

  1. 当文件大小小于 limit 值时,url-loader 将会把文件转为 Data URLs;
  2. 当文件大小大于 limit 值时,url-loader 会默认调用 file-loader 进行处理,参数也会直接传给 file-loader。

简单地说,url-loader 封装了 file-loader。但 url-loader 不依赖于 file-loader,通过配置 fallback 参数,我们可以灵活地设定 fallback Loader。在实际项目中,通过配置 url-loader 的 limit 参数,我们就可以把一些静态资源转成 Data URLs,从而避免过多的 HTTP 请求,从而提高页面的性能。当然 limit 参数也不能设置得太大,不然也会导致加载的资源过大,影响页面的加载速度。

参考资源

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏城边编程

利用CSS劫持流量

最近正好有一些时间用来帮大厂挖掘漏洞,也就有了今天的话题。为什么会想到去帮互联网公司挖掘漏洞呢?一是想为互联网的美好明天贡献微薄之力,二是保证持续学习的心态,三...

10320
来自专栏HACK学习

记一次渗透某XX站

开burp抓了下包,目标设置了url重写,开了报错,我们随意输入一个控制器就直接报错。

16630
来自专栏陶士涵的菜地

[PHP] PHP-FPM的access日志error日志和slow日志

PHP-FPM的错误日志建议打开,这样可以看到PHP的错误信息: 一般是这个配置路径 /etc/php/7.3/fpm/pool.d/www.conf,日志目录...

9500
来自专栏GA小站

2.2.1、Google Analytics高级应用——过滤器的应用

过滤器用于限制和修改数据视图中包含的数据。可以使用过滤器实现以下目的:排除来自特定IP地址的流量,仅包含某个子网域或目录中的数据,或者将动态网页网址转换为可读的...

5530
来自专栏软件测试testclass

Python笔记:Django框架做web开发(二)

上一篇文章,主要做了以下内容: 1.安装Django,搭建开发环境; 2.创建了一个项目mysite; 3.成功启动了Django开发服务器; 4.成功的访问了...

9610
来自专栏SAS程序分享号号号

SAS-函数(一),总把新桃换旧符~

做为一个曾经写了30+临床项目的数据清洗的SAS程序的小编打算本文将围绕数据清洗中的SAS函数应用展开。当然文中涉及的到例子SAS实现的方法很多可能并非是最佳方...

13220
来自专栏渗透云笔记

记一次对钓鱼网站的多次渗透

0x01 首先我们对目标进行目录扫描,发现admin.php 进入发现是后台界面,右击查看网页源码

33120
来自专栏用户6510507的专栏

最新网站被挂马被跳转解决办法

最近收到一位客户的反馈,告知网站又被挂马,(织梦程序真让人头疼总是被挂马,dedecms经常是被挂马真晕了是的~)相信站长们都有遇到过网站被挂马或代码恶意植入的...

27600
来自专栏PHP饭米粒

PHP+Swoole并发编程的魅力

PHP语言是一个短生命周期的Web编程语言,很多PHPer已经形成了fpm下编程的思维定势。实际上在Swoole出现之后,这种串行化编程的模式早已被打破。使用S...

8420
来自专栏SH的全栈笔记

注册微信开发测试号

要进行微信公众号的开发,那就需要一个本地的开发环境来进行开发。而微信测试号就正好提供了这样的一个development环境。每个微信号只能对应一个测试号,但是每...

18540

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励