Webpack 下页面的 2x 图片解决方案

随着现在高分辨率显示器(2K,4K)的普及,以及移动设备上屏幕的ppi的提高,网页上提供高分辨率的图片显得越来越重要。

在用 Webpack 构建的项目中,常见的解决方式,就是提供一张高分辨率的图片,然后在编译时压缩生成对应的低分辨率的版本,这种方法处理较为简单,但是也有一些问题:图片压缩只能是固定的压缩比例;压缩质量不好控制;代码中使用的时候诸多不便;

而现在我们在设计的时候,一般都直接提供了 1x 2x 甚至是 3x 的图片素材,也就是说,压缩这一步骤已经没有太多的必要。

一开始的思路

所以一个简单的思路就是,按照 original.png original@2x.png 这样的形式去命名图片,然后页面中去引用,低分辨率用 original.png,高分辨率用 original@2x.png。配合 file loader,实现起来非常简单,require 1x 的图片,然后利用 copy-webpack-plugin将2x的图片复制到相同的目录,最后需要用 2x 的图片的时候,直接替换文件路径就可以。

这种方法有一个问题,就是 2x 图片不好管理,因为需要与 1x 图片的名字前缀保持一致,也就无法对 1x 图片文件名进行 hash,对于缓存控制不太好。

于是我就想,能不能在 resolve 1x 图片的同时,将 2x 图片也一起 resolve 并且把 2x 图片的 resolve 结果也返回,这样在代码中就可以同时得到 1x 和 2x 图片的引用,可以自由使用和组合。

ideal-image-loader

找了一下,发现已经有 ideal-image-loader 这个开源的项目了,它的逻辑就是和我上面说的一样,配合 srcset,可以轻松的解决 2x 图片的问题。

import image from './abc.png';

/*
image:
{
    x1: {
        src: "",
    },
    x2: {
        src: "",
    },
}
*/

const srcset = `${image.x1.src} x1, ${image.x2.src} x2`
<img srcset={srcset} />

这样就实现了自动的图片 2x 控制。对于开发者来说也是无感的,非常方便。

后续的一些问题

  1. 由于 ideal-image-loader 和 file-loader 的返回格式不一样,导致有些想要直接引用图片的地方不能正常工作了 常见的就是

    .a {
        background: url(./assets/example.png);
    }
    

    这些背景图片直接写在 css 中,不需要使用 2x 图片,因此只要 file-loader 就可以了。

    这时有两种方案可以选择:将这些图片放到特定的文件夹,调整 rule 的include 和 exclude;或者可以通过 rule 的 issuer,所有在 css 中引入的图片,都使用 file-loader 加载。