npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-img-worker

v1.1.5

Published

图片加载占据了一个 web 应用的较多资源。

Readme

图片加载占据了一个 web 应用的较多资源。

这篇文章中,我们实现一个 ImgWorker 组件,满足以下功能:

  • image 可选择的 worker 加载,并且做好优雅降级
  • decode 解码,并且做好优雅降级
  • miniSrc 和 src 竞速加载

注意,在性能较低的机器,开启 worker 进程下载的开销大于直接使用主进程下载的开销; 使用 worker 需要主动传递 worker 属性为 true 开启

组件代码

先上代码,后续对关键代码做一些解释:

src/react-img-worker.js :

import * as React from 'react';

const blobUrl = new Blob(
  [
    `
self.addEventListener('message', event => {
  const [url, type] = event.data;
  fetch(url, {
      method: 'GET',
      mode: 'no-cors',
      cache: 'default'
  }).then(response => {
      return response.blob();
  }).then(_ => postMessage([url, type])).catch(console.error);
})
`,
  ],
  { type: 'application/javascript' }
);

interface IImgWorkerProps
  extends React.DetailedHTMLProps<
    React.ImgHTMLAttributes<HTMLImageElement>,
    HTMLImageElement
  > {
  boxProps?: React.DetailedHTMLProps<
    React.ImgHTMLAttributes<HTMLImageElement>,
    HTMLImageElement
  >;
  miniSrc?: string;
  objectFit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down';
  renderLoading?: any;
  worker?: boolean;
}

interface IImgWorkerState {
  isLoading: boolean;
  src: string;
}

export class ImgWorker extends React.Component<
  IImgWorkerProps,
  IImgWorkerState
> {
  public static defaultProps = {
    objectFit: 'cover',
  };

  public div: any = null;
  public image: HTMLImageElement = new Image();
  public isLoadedSrcLock = false;
  public state = {
    isLoading: true,
    src: '',
  };
  public worker: Worker = null as any;
  public constructor(props: IImgWorkerProps) {
    super(props);
    this.image.style.width = '100%';
    this.image.style.height = '100%';
    this.image.style.display = 'none';
    this.image.style.objectFit = this.props.objectFit!;

    // 如果使用 worker 并且浏览器支持 worker
    if (this.props.worker && typeof Worker !== 'undefined') {
      this.worker = new Worker(URL.createObjectURL(blobUrl));
      this.worker.addEventListener('message', event => {
        const [url, type] = event.data;

        this.loadImage(url, type);
      });
    }
  }

  public componentDidMount() {
    this.div.appendChild(this.image);
    this.postMessage(this.props);
  }

  public componentWillReceiveProps(nextProps: IImgWorkerProps) {
    if (nextProps.objectFit !== this.props.objectFit) {
      this.image.style.objectFit = nextProps.objectFit!;
    }
    let isPostMessage = false;
    if (nextProps.miniSrc !== this.props.miniSrc) {
      isPostMessage = true;
    }
    if (nextProps.src !== this.props.src) {
      isPostMessage = true;
    }

    // 如果 src 或 miniSrc 更新,重新请求
    if (isPostMessage) {
      this.isLoadedSrcLock = false;
      this.postMessage(nextProps);
    }
  }

  public componentWillUnmount() {
    if (this.image) {
      this.image.onload = null;
      this.image.onerror = null;
    }
    if (this.worker) {
      this.worker.terminate();
    }
  }

  public loadImage = (url: string, type: string) => {
    // 如果 src 已经被设置,拦截后续的更新
    if (this.isLoadedSrcLock) {
      return;
    }
    if (type === 'src') {
      this.isLoadedSrcLock = true;
    }

    this.image.src = url;
    this.image.decoding = 'async';
    this.image.decode !== undefined
      ? this.image
          .decode()
          .then(this.onLoad)
          .catch(this.onLoad)
      : (this.image.onload = this.onLoad);
  };

  public onLoad = () => {
    this.image.style.display = 'block';
    this.setState({
      src: this.image.src,
      isLoading: false,
    });
  };

  public postMessage = (props: IImgWorkerProps) => {
    if (props.miniSrc) {
      if (this.worker) {
        this.worker.postMessage([props.miniSrc, 'miniSrc']);
      } else {
        this.loadImage(props.miniSrc, 'miniSrc');
      }
    }

    if (props.src) {
      if (this.worker) {
        this.worker.postMessage(this.worker.postMessage([props.src, 'src']));
      } else {
        this.loadImage(props.src, 'miniSrc');
      }
    }
  };

  public render() {
    const { boxProps, renderLoading: Loading, src: _src, ...rest } = this.props;
    const { isLoading } = this.state;

    return (
      <div ref={r => (this.div = r)} {...rest}>
        {Loading && isLoading && (
          <Loading key="img-worker-loading" isLoaing={isLoading} />
        )}
      </div>
    );
  }
}

代码是 typescript 编写的,这是为了组件发版可以更简便的生成.d.ts 文件,内容很简单,其中关键在于两处:

  1. 创建一个 worker
this.worker = new Worker(URL.createObjectURL(blobUrl));
  1. 使用 decode

decode 的功能网上有许多文章,这里不展开

const image = new Image();
image.src = url;
image.decoding = 'async';
image.decode !== undefined
  ? image
      .decode()
      .then(this.onLoad)
      .catch(this.onLoad)
  : (image.onload = this.onLoad);

使用

使用默认 image loader, ImgWorker 保留了 <img /> 标签原有的所有 api

import { ImgWorker } from './react-img-worker';

export default () => {
  return <ImgWorker src="http://example.png" />;
};

使用 worker loader:

import { ImgWorker } from './react-img-worker';

export default () => {
  return <ImgWorker worker src="http://example.png" />;
};

支持 mini 图片,如果 设置了 miniSrc 会并行加载 miniSrc 和 src;若 miniSrc 优先加载完,显示 miniSrc;若 src 优先加载完会拦截此次后续的图片更新。

使用 worker + miniSrc + src:

import { ImgWorker } from './react-img-worker';

export default () => {
  return (
    <ImgWorker
      worker
      miniSrc="http://example.mini.png"
      src="http://example.png"
    />
  );
};

直接使用 react-img-worker 库

为了方便使用,以上代码已发布至 npm,可以直接使用

$ yarn add react-img-worker

希望有对君有帮助:)