加载图片

加载大量小的资源会使基于HTTP/1 的应用变慢,因为每个请求都会产生开销。HTTP/2 在这方面有所改善,并且在某种程度上改变了这种情况。除此之外,Webpack 也有一些解决办法。

Webpack可以使用 url-loader 内联资源。它会将您的图像转为 base64 形式的字符串,从而减少 HTTP 请求,但是它会增加打包结果的尺寸。在开发过程中,我们可以使用这种方式,生产环境可能不太适合。

Webpack 可以控制内联过程,然后使用 file-loader 对其延迟加载。file-loader 能够将导入的图片存放到特定的目录,并返回图片的路径地址。此技术适用于其他类型的资源,例如字体,后面的章节也会提到。

设置 url-loader

url-loader 是一个很好的起点,它是开发环境的完美选择,因为您不必关心生成的 bundle 的大小。它带有一个 limit 选项,当达到绝对限制后将图像交给 file loader 处理。这样,您可以将小文件内联到 JavaScript 包中,同时为较大的文件生成单独的请求地址。

如果要使用 limit 选项,则需要在项目中安装 url-loaderfile-loader。假设您已正确配置样式,webpack 将解析样式中任何包含 url() 的语句。您也可以通过 JavaScript 代码指向图像资源。

在使用 limit 选项的情况下,url-loader 将可能的附加选项传递给 file-loader,从而可以让 file-loader 进一步配置其行为。

要在内联 25kB 以下的文件时加载 .jpg.png 文件,您可以这样设置 loader:

{
  test: /\.(jpg|png)$/,
  use: {
    loader: "url-loader",
    options: {
      limit: 25000,
    },
  },
},

当 limit 限制生效时,如果你想使用另一个 loader,而不是 file-loader 的话,你可以在配置中添加 fallback: "some-loader"。然后 Webpack 就会使用这个 loader 而不是采取默认行为。

设置 file-loader

如果要完全跳过内联,可以直接使用 file-loader。以下配置自定义了生成的文件名。默认情况下,file-loader 使用文件内容的 MD5 哈希值和扩展名组合称新的文件名:

{
  test: /\.(jpg|png)$/,
  use: {
    loader: "file-loader",
    options: {
      name: "[path][name].[hash].[ext]",
    },
  },
},

如果要将图像输出到特定目录下,请在配置中这样设置 name: "./images/[hash].[ext]"

注意不要同时在图像上同时应用两个 loader!如果 url-loader limit 不够,请使用 include 字段进一步控制。

将图像集成到项目中

现在我们可以将上述想法在本书的项目中开始实践,首先安装依赖项:

npm install file-loader url-loader --save-dev

并设置以下配置:

webpack.parts.js

exports.loadImages = ({ include, exclude, options } = {}) => ({
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        include,
        exclude,
        use: {
          loader: "url-loader",
          options,
        },
      },
    ],
  },
});

要将这部分配置合并到主配置中,请按如下方式进行调整。开发期间默认使用 url-loader,生产中使用 url-loaderfile-loader 来产生一个较小的 bundle。url-loader 在设置 include 时隐式使用 file-loader,并且必须安装两者才能使配置起作用。

webpack.config.js

const productionConfig = merge([
  ...

  parts.loadImages({
    options: {
      limit: 15000,
      name: "[name].[ext]",
    },
  }),

]);

const developmentConfig = merge([
  ...

  parts.loadImages(),

]);

要测试配置是否有效,我们可以下载或生成(convert -size 100x100 gradient:blue logo.png)一张图像,并从项目中引用它:

src/main.css

body {
  background: cornsilk;

  background-image: url("./logo.png");
  background-repeat: no-repeat;
  background-position: center;

}

构建行为会根据您设置的 limit 而变化。低于限制,它应该内嵌图像;而高于限制,它应该保存一份单独的文件并返回该文件的路径。CSS 文件的查找因 css-loader 而起作用。您还可以尝试从 JavaScript 代码导入图像,看看会发生什么。

加载 SVG

Webpack 允许几种方式加载 SVG。但是,最简单的方法是通过 file-loader,如下所示:

{
  test: /\.svg$/,
  use: "file-loader",
},

现在,我们就可以在文件中引入 SVG 了。下面的例子中,SVG 路径是相对于 CSS 文件的:

.icon {
   background-image: url("../assets/icon.svg");
}

我们还可以考虑以下 loader:

  • raw-loader 允许访问原始 SVG 内容。
  • svg-inline-loader 更进一步,消除了 SVG 中不必要的标记。
  • svg-sprite-loader 可以将小的 SVG 文件合并为一个 sprite 文件,从而可以更有效地加载,因为您可以避免请求开销。它也支持 .jpg.png 格式的图像。
  • svg-url-loader 将 SVG 加载为 UTF-8 编码的数据 URL。结果比 base64 更小,解析更快。
  • react-svg-loader 可以将 SVG 作为 React 组件发出,这意味着您可以在代码中导入它们,并可能会以这样的编码形式(<Image width={50} height={50}/>)来渲染 SVG 元素。

您也可以使用按照之前的思路,使用 url-loader 来处理 SVG。

优化图像

如果您想压缩图像,请使用 image-webpack-loadersvgo-loader(专用于 SVG)或 imagemin-webpack-plugin。注意,此类型的 loader 应用于最终的数据,因此请记住,将其它们作为 use 列表中的最后一个来执行。

压缩对于生产构建特别有价值,因为它减少了下载图像时所需的带宽量,从而加快了站点或应用程序的速度。

利用 srcset

resize-image-loaderresponsive-loader 允许您为现代浏览器生成 srcset 属性所需要的图像集合。srcset 属性可以让浏览器决定加载哪些图像以及何时提高性能。

动态加载图像

Webpack 允许您根据条件动态加载图像。“代码拆分”“动态加载”章节中介绍了这些技术细节。这样做可以节省带宽,并仅在需要时加载图像或在有时间的情况下预加载图像。

加载 Sprite

Spriting 技术允许您将多个较小的图像组合成单个图像。它已经被用于游戏中的动画,但对于 Web开发也很有价值,因为它节省了很多请求开销。

webpack-spritesmith 将提供的图像转换为 sprite 图和一些 Sass/Less/Stylus mixin。您必须设置 SpritesmithPlugin 插件,将其指向目标图像,并设置生成的mixin 的名称。之后,你的 样式就可以使用它了:

@import "~sprite.sass";

.close-button {
  sprite($close);
}

.open-button {
  sprite($open);
}

使用图片占位符

image-trace-loader 加载图像并将结果编码为 image/svg+xml 格式的 URL 数据。它可以与 file-loaderurl-loader 一起使用,以便在加载实际图像时显示占位符。

lqip-loader 实现了类似的功能,它不显示占位符,而是提供一个模糊的图像。

获得图像尺寸

有时只获得对图像的引用是不够的。除了返回图像本身的引用之外,image-size-loader 还会返回图像尺寸、类型和大小。

引用图像

如果配置了好了 css-loader,Webpack 可以通过 @import@import 从样式表中获取图像。JS 代码中也可以引用图像。在这种情况下,您必须在文件中显式地导入:

import src from "./avatar.png";

// 在代码中可以使用图片了
const Profile = () => <img src={src} />;

如果您使用的是 React,那么您可以使用 babel-plugin-transform-react-jsx-img-import 自动生成 require。在这种情况下,您的代码可以精简成下面的样子:

const Profile = () => <img src="avatar.png" />;

代码拆分一章所述,也可以设置动态导入。下面一个小例子:

const src = require(`./avatars/${avatar}`);

启用图像和 css-loader 的 Source Map

如果在启用图像和 css-loadersourceMap 选项,则要将 output.publicPath 设置为开发服务器的绝对地址。否则,图像无法正常工作。请参阅有关 Webpack 的问题了解更详细的内容。

总结

Webpack 允许您在需要时在打包结果中内联图像。您可以不断的实验,然后找到一个合适的内联限制。您必须在包大小和请求数之间取得平衡。

回顾一下:

  • url-loader 在 JavaScript 中内联资源。它附带一个 limit 选项,允许您将超过 limit 的资源交给 file_loader 处理。
  • file-loader 发出图像资源并将它们的路径返回到代码。它可以为图片设置哈希值名称。
  • 您可以找到与图片优化相关的加载器和插件,以便您进一步调整其大小。
  • 可以从较小的图像生成 sprite,以将它们组合成单个请求。
  • Webpack 允许您根据给定条件动态加载图像。
  • 如果您要使用 Source Map,则应记住设置 output.publicPath 设置为开发服务器的绝对地址,这样图片才能够正常显示。

您将学习在下一章中使用 Webpack 加载字体。

用户头像
登录后发表评论