图片占位符 lqip 简介与使用

799 阅读3分钟

LQIP

图片占位符大家肯定不陌生。现在主流的网站,例如博客 Medium、知乎等都支持了从模糊到清晰的图片加载效果,带来很棒的产品浏览体验。

image.png

LQIP 是 Low Quality Image Placeholders 的缩写,就是在这背后默默做出贡献的基础技术。这个技术可以根据原始图片自动生成超小尺寸的缩略图。

image.png

zouhir/lqip: Low Quality Image Placeholders (LQIP) Module for Node

与之相关的还有 SQIP,就不多讲了。相关资料:

直接使用 lqip 来维护图片是一件很痛苦的事情,所以不如利用前端工程的方法将其自动化。而 lqip-loader 是用其导入集成到 webpack 的 loader,进而不需要我们手动处理相关的文件内容,只需从源码里导入和使用即可。

zouhir/lqip-loader: Low Quality Image Placeholders (LQIP) for Webpack

配置方案

直接使用 lqip-loader 集成到 webpack 的方案:

配置方案一

使用 file-loader

{
  /**
   * 配置方案一
   * 使用 file-loader
   **/
  test: /\.(png|jpe?g)$/,
    loaders: [
      {
        loader: 'lqip-loader',
        options: {
          path: '/media', // your image going to be in media folder in the output dir
          name: '[name].[ext]', // you can use [hash].[ext] too if you wish,
          base64: true, // default: true, gives the base64 encoded image
          palette: true // default: false, gives the dominant colours palette
        }
      }
    ]
}

配置方案二

结合其他 loader

{
  /**
   * 配置方案二
   * 结合其他 loader
   **/
  test: /\.(png|jpe?g)$/,
    loaders: [
      {
        loader: 'lqip-loader',
        options: {
          base64: true,
          palette: false
        }
      },
      {
        loader: 'url-loader',
        options: {
          limit: 8000
        }
      }
    ]
}

集成到 Umi

本人使用的脚手架是 Umi ,因此需要用其提供的 chainwebpacklqip-loader 导入。

通过翻阅了其源码(umi/getConfig.ts at master · umijs/umi),可以知道图片处理相关的规则集为images 然后需要在 url-loader 前将 lqip-loader 导入。

配置如下:

 chainWebpack: (config: Config) => {
    config.module
      .rule('images')
      .use('lqip-loader')
      .loader('lqip-loader')
      .before('url-loader');
    return config;
  },

使用方法

当直接 import 一张图片时,会有三个参数 srcpreSrcpalette(可选)

接口签名

interface LqipImage {
  src: string;
  preSrc: string;
  palette?: string[];
}

简单测试:

import banner from './images/banner.jpg';

console.log(banner.preSrc);
// 输出: "data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhY.... 

// banner 将具有调色板属性(在配置项中开启 palette: true ),数组将从最主要的颜色排序到最少
console.log(banner.palette)
// 输出: [ '#628792', '#bed4d5', '#5d4340', '#ba454d', '#c5dce4', '#551f24' ]
 
console.log(banner.src) // 原图片 url

案例

由于 import 之后的图片结构做了变动,因此个人建议自行在已有的图片组件基础上封装一个集成 lqip 输出物的图片组件。

封装的思想很简单,就是对传入的 src 做个判断,如果是字符串,返回原来你自己使用的图片组件,如果是个对象,那么自行处理 srcpreSrc 的相关逻辑。

本人选择搭配 react-simple-img 使用,这个组件集成了懒加载、图片占位符、响应式加载图片、响应式尺寸等能力(预览 Demo :React Simple Image)。

我自行封装的 LazyImage 如下:

import { SimpleImg } from 'react-simple-img';
import React, { FC } from 'react';

interface LazyImageProps {
  src: AdaptiveImage;
  height?: number | string;
  width?: number | string;
  placeholder?: string | boolean;
  className?: string;
  alt?: string;
  sizes?: string;
}

const LazyImage: FC<LazyImageProps> = ({
  src,
  height,
  width,
  placeholder,
  className,
  alt,
  sizes,
}) => {
  if (typeof src === 'string') {
    return (
      <SimpleImg
        placeholder={placeholder}
        src={src}
        sizes={sizes}
        alt={alt}
        height={height}
        width={width}
        className={className}
      />
    );
  }

  return (
    <SimpleImg
      placeholder={src.preSrc}
      src={src.src}
      alt={alt}
      height={height ? height : undefined}
      width={width}
      className={className}
    />
  );
};
export default LazyImage;

原本使用 SimgleImg 的代码如下:

<SimpleImg
  src={img}
  width={width}
  placeholder={false} // 不使用占位符
  height={imageHeight ? imageHeight : height}
  />

使用了 lqip 之后,直接无痛切换到自己封装的 LazyImage

<LazyImage
  src={img}
  width={width}
  height={imageHeight ? imageHeight : height}
  />

这样就实现了极低开发成本的模糊加载能力。