React里面引入本地图片及懒加载

3,175 阅读2分钟

图片引入

react组件里面需要引入本地图片,按照通常的方式, <img src="../../assets/images/default-avatar.jpg"/> 引入肯定是不行的。

react提供的引用本地图片的方式:

1. 通过import引入

import defaultAvatar from '../../assets/images/default-avatar.jpg'
...
<img src={defaultAvatar}/>  //img中直接使用变量即可

这种引入方式,webpack把当前图片当做资源文件打包,可以自己在配置文件里面设置图片加载器,小于多少kb用base64的格式打包;当大于指定kb时,webpack会把当前图片也编译到打包目录下面。

2. 使用require引入

<img src={require("../../assets/images/default-avatar.jpg")}/>

这种方式,可以在css文件里面使用,因为css-loader会把资源文件一起打包,而在js文件中这样引入,webpack只会把当前的src当做字符串,并不会当做资源文件去处理;当项目打包到线上时,就会出现资源文件路劲找不到的问题。

按照这个说法,本地应该还是可以使用这种引入方式,但是这个方法,在我自己的本地项目里面使用时, img标签的src里面显示的是这样的一个值。

查询之后了解到,使用create-react-app创建了react项目,使用require引入图片文件时,require返回的是一个ES模块,而不是字符串。这是因为在file-loader中,esModule是默认启动的。

所以这种方式需要改写为

const imgSrc = require("../assets/image/whitehouse.jpeg").default;
<img src={imgSrc}/>

不过这种方法,打包的时候还是可能会存在路径找不到的问题,还是建议使用import来引入本地图片。

图片懒加载

既然要加载图片,那么当页面上存在大量图片的时候,就存在需要懒加载图片的问题。

1. 监听window.scroll事件

定义lazyLoad方法,监听window.scroll事件。

import defaultAvatar from '../../assets/images/default-avatar.jpg'
...

componentDidMount() {
    window.addEventListener('scroll', this.lazyLoad)
}

componentWillUnmount() {
    window.removeEventListener('scroll', this.lazyLoad);
}
lazyLoad(){
    this.setState({imgSrc:defaultAvatar})
}

render(){
    return <img src={this.state.imgSrc}/>
}

不过这种方式,只要页面开始滚动,就会加载图片。页面上加载的图片较多时,就没有了优势。

2. 使用getBoundingClientRect Scroll + with Throttle + DataSet API

引入一个新的API Element.getBoundingClientReact() 方法返回元素的大小及其相对于视口的位置。

img.getBoundingClientRect().top < document.documentElement.clientHeight

监听window.scroll的事件优化一下,加个节流器提高性能。lodash

import _ from 'lodash';
import throttle from 'lodash/throttle';
...
constructor(props){
	super(props);
	this.lazyLoad = _.throttle(this.lazyLoad.bind(this), 300);
	this.lazyLoad = throttle(this.lazyLoad.bind(this), 300);
}

3. 使用html5的IntersectionObserver API监听元素是否到了当前视口。

constructor(){
	this.myRef = React.createRef();
}
componentDidMount() {
       const observer = new IntersectionObserver((changes) => {
            changes.forEach(change => {
                if (change.isIntersecting) {
                   this.myRef.current.src = this.props.imgSrc;
                   observer.unobserve(this.myRef.current);
                }
            });
        });
        observer.observe(this.myRef.current);
    }
...
render() {
        return <div className="img-wrapper">
            <img src={this.state.imgSrc} ref={this.myRef} data-src={this.state.imgSrc} loading="lazy"/>
        </div>
    }

4. LazyLoading属性

<img src="imgSrc" loading="lazy">

兼容性不太好,慎用。

react获取DOM元素

这里补充一个react获取dom元素的方法

  • 原生js获取dom元素,ById ByClassName querySelector等
  • react 自带的 refs 方式 这个官方推荐使用 但不建议使用在事件源上面
  • 事件对象 event 来使用 通常用于事件源

参考链接