代理模式+react+ 图片占位图

在项目中可能有这种需求,我们需要对一组图片使用swiper组件,让它成为幻灯片可以滑动,但是需要先将图片下载之后才init swiper。所以如果网络卡的话,会出现容器塌陷,当图片载入成功时,然后容器又从塌陷变成有高度状态,用户体验很不好。下面使用react组件方式来解决这个问题。

流程:

graph LR ajax请求获取图片数据-->占位图片--> 加载图片时显示占位 --> 加载完成后-->替换占位符并初始化swiper插件

考虑到有多张图片,什么时候图片完全加载成功呢?可以使用 Promise.all来解决多个图片的加载成功的监听。 先写加载图片的loadImgs.js



function load(url){
	
	return new Promise((resolve,reject)=>{
        let img =new Image()
		img.src=url;
		img.onload=()=>{
			resolve()
		}
		img.onerror=()=>{
			reject();
		}
	})
}

function loadImgs(imgs){
	if(imgs.length==0){
		return Promise.reject();
	}
	let promisesImg =imgs.map(item=>(
		load(item)
 	) );

	return  Promise.all(promisesImg)
}

export default loadImgs

复制代码

然后处理占位图片的问题,这里可以使用代理模式。控制对真实图片的展示和隐藏

为了复用占位组件,这里使用高阶组件。将真实图片展示组件作为它的子元素


/**
 *  做一层代理 proxy
 * @param {*} BasesComponent 基组件展示图片的或者包含图片的块
 * @param {*} imageKey 图片字段
 */
 import loadImgs from './loadImgs'
export let EnhanceBSwiper=function(BasesComponent,imageKey=images_keys){
    const wrappedDisplayName = BasesComponent.name;

    return class  extends Component {
         //便于调试
        static displayName = `Enhance(${wrappedDisplayName})`;

        constructor(props){
            super(props)
            this.state={
                init: false
            }
            if(props.images.length>0){
                //ajax api 请求已经完成
                this.loadImg(props.images);
            }
        }

        loadImg(images){
            loadImgs(images).then(()=>{
                this.setState({
                    init:true
                })
                console.log('ok')
             },()=>{
                 //图片载入失败重试
                // location.reload();
                alert('imgs load error')
             })
        }
        componentWillReceiveProps(nextProps) {
            if(nextProps[imageKey].length!=this.props[imageKey].length){
                 this.loadImg(nextProps[imageKey])
            }
         }

         render(){
             let {init}=this.state;
             if(!init){
                 return <div className="placeholder">
                            
                        </div>
             }
             return <BasesComponent {...this.props} images={this.props[imageKey]}/>
         }
     
    }
}

复制代码

使用,现在有一个BSwiper组件。


export  class BSwiper extends Component {
    
    componentDidMount() {
       setTimeout(this.newSwiper,200)
    }

    newSwiper(){
        let swiper = new Swiper('.swiper-container', {
            pagination: '.pagination',
            loop:true,
            paginationClickable: true,
            paginationBulletRender: function (swiper, index, className) {
                return '<span class="' + className ||"swiper-pagination-bullet" + '">' + (index + 1) + '</span>';
            }
        });
    }
   

    render() {
        let { images} =this.props;
        return (
            <div className="swiper-container">
                        <div className="swiper-wrapper js_banner_list">
                        { this.props.images.map( (item,index)=>(
                            <div className="swiper-slide" key={index}>
                              <img src={item} alt="" />
                            </div>
                        ))}
                        </div>
                        <div className="pagination"></div>
                    </div>
        );
    }
}
复制代码

使用时

let BEnhanceBSwiper = EnhanceBSwiper( BSwiper ,'images');
  
  <BEnhanceBSwiper images={images} />
 
复制代码

这样就拆分了功能,BSwiper组件只关心图片的初始化swiper,由上层组件来决定它什么时候初始化渲染。 上面那个高阶组件职责,加载所有图片前,初始化一个占位符,等加载完成后,再render真实的业务展示组件。至于展示什么业务组件,则由BaseComponent来决定。它可能是一个包含图片的列表,或者一个swiper。分离了职责。提高了维护性。 缺点:对某些图片没有情求成功做处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值