代理模式+react+ 圖片佔點陣圖

假裝做前端發表於2018-08-30

在專案中可能有這種需求,我們需要對一組圖片使用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。分離了職責。提高了維護性。 缺點:對某些圖片沒有情求成功做處理

相關文章