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