日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
關(guān)于圖片加載,你需要學(xué)習(xí)一下

從何而來

這篇文章,出自我自己的開源組件庫 fighting-design[2] 中的 Avatar 頭像[3] 組件的 load-image[4] 類。

成都創(chuàng)新互聯(lián)公司服務(wù)緊隨時(shí)代發(fā)展步伐,進(jìn)行技術(shù)革新和技術(shù)進(jìn)步,經(jīng)過10多年的發(fā)展和積累,已經(jīng)匯集了一批資深網(wǎng)站策劃師、設(shè)計(jì)師、專業(yè)的網(wǎng)站實(shí)施團(tuán)隊(duì)以及高素質(zhì)售后服務(wù)人員,并且完全形成了一套成熟的業(yè)務(wù)流程,能夠完全依照客戶要求對網(wǎng)站進(jìn)行網(wǎng)站制作、成都網(wǎng)站制作、建設(shè)、維護(hù)、更新和改版,實(shí)現(xiàn)客戶網(wǎng)站對外宣傳展示的首要目的,并為客戶企業(yè)品牌互聯(lián)網(wǎng)化提供全面的解決方案。

相比于其它的靜態(tài)組件,像圖片加載這種的組件,內(nèi)部我做了很多的優(yōu)化,對于圖片的加載和錯(cuò)誤的處理,我都盡可能的將每種可能出現(xiàn)的結(jié)果都考慮到,針對每種不確定的結(jié)果做出相應(yīng)的提示,以便于提升用戶體驗(yàn)。

 設(shè)計(jì)思路

我的設(shè)計(jì)想法是:通過一個(gè)加載類,傳入 dom 元素、 props 和 emit。先創(chuàng)建出一個(gè)虛擬的 image 元素進(jìn)行嘗試加載,加載成功獲失敗都會進(jìn)入下一步的函數(shù),做出對應(yīng)從處理邏輯。

初步設(shè)計(jì)

首先類中先有一個(gè)加載的方法 loadCreateImg,代碼如下:

class Load {
constructor(node, props, emit) {
this.node = node
this.props = props
this.emit = emit
}
// 加載 src
loadCreateImg = () => {
const newImg = new Image() // 新建一個(gè)虛擬的 img
newImg.src = this.props.src // 將傳入的 src 賦值給虛擬節(jié)點(diǎn)
// src 加載失敗
newImg.addEventListener('error', (evt) => {
// 加載失敗的處理
})
// src 加載成功
newImg.addEventListener('load', (evt) => {
// 加載成功的處理
})
}
}
復(fù)制代碼

首先我創(chuàng)建了一個(gè) Load 的加載類,需要傳入 node 參數(shù)作為最終需要渲染的 dom 節(jié)點(diǎn),props 是傳入的組件內(nèi)部的 props 參數(shù),內(nèi)部包含圖片需要加載的 src 路徑,emit 包括一些回調(diào)參數(shù)。

類的內(nèi)部有個(gè) loadCreateImg 的方法,調(diào)用可創(chuàng)建一個(gè)虛擬的 Image 元素,直接將傳入的 props.src 賦值并加載。監(jiān)聽上面的 error 和 load 事件,即可監(jiān)聽到圖片是否加載成功,以便做出不同的狀態(tài)。

成功和失敗

對于成功或失敗的處理,我新增了 onerror 和 onload 方法,來處理加載成功和失敗之后的不同處理狀態(tài)

class Load {
constructor(node, props, emit) {
this.node = node
this.props = props
this.emit = emit
}
loadCreateImg = () => {
const newImg = new Image()
newImg.src = this.props.src
newImg.addEventListener('error', (evt) => {
this.onerror(evt) // 新增
})
newImg.addEventListener('load', (evt) => {
this.onload(evt) // 新增
})
}
// 加載成功
onload = (evt) => {
this.node.src = this.props.src
}
// 加載失敗
onerror = (evt) => {
// ……
}
}
復(fù)制代碼

對于加載成功,處理方式是,將傳入的真是的 dom 節(jié)點(diǎn)直接賦值給傳入的 props.src 即可完成加載。

加載失敗

對于加載失敗的處理,F(xiàn)ighting Design 內(nèi)部做了很多處理,比如可以傳入 err-src 的備用路徑加載,在 src 加載失敗之后,如果 err-src 存在的話,那么就需要加載 err-src 。接下來繼續(xù)完善類方法:

首先要在 onerror 方法中判斷是否存在 err-src,如果有 err-src 那么就需要重新調(diào)用 loadCreateImg 重新加載,但是現(xiàn)在的代碼顯然不能滿足需要,所以 loadCreateImg 需要接收一個(gè)可選的參數(shù)為 errSrc,因?yàn)橹挥性诩虞d失敗之后才需要再次調(diào)用該方法傳入 err-src,所以方法內(nèi)部就可以根據(jù) err-src 是否存在,來做出不同的處理:

class Load {
constructor(node, props, emit) {
this.node = node
this.props =
this.emit = emit
}
loadCreateImg = (errSrc?: string) => {
const newImg = new Image()
// 如果 errSrc 存在 就嘗試加載 errSrc
if (errSrc) {
newImg.src = errSrc
} else {
newImg.src = this.props.src
}
newImg.addEventListener('error', (evt) => {
this.onerror(evt)
})
newImg.addEventListener('load', (evt) => {
this.onload(evt)
})
}
onload = (evt) => {
this.node.src = this.props.src
}
// 加載失敗
onerror = (evt) => {
// 如果存在 errSrc 則繼續(xù)嘗試加載
if (this.props.errSrc) {
// 將 errSrc 傳給 loadCreateImg 方法
return this.loadCreateImg(this.props.errSrc)
}
// 否則返回失敗回調(diào)
this.emit('error', evt)
}
}
復(fù)制代碼

但是上面代碼存在兩個(gè)問題:

  1. 首先我們發(fā)現(xiàn),在 `onload` 加載成功的方法中,將真實(shí) `dom` 始終賦值的始終 是 `src`:
onload = (evt) => {
// 始終賦值為 props.src
this.node.src = this.props.src
}
復(fù)制代碼

但是 src 并不是始終可以加載成功的,所以還是需要動態(tài)的去將真正加載成功的 src 傳給 onload 方法,那么真正加載成功的 src 也就是在 load 方法中。并且還要加入成功的 emit。

  1. 其次,在處理加載失敗的 `onerror` 方法中,因?yàn)榕袛嗔巳绻嬖?`errSrc` 就繼續(xù)調(diào)用 `loadCreateImg` 加載方法重新加載。問題是,如果傳入了 `errSrc` 那么 `if (this.props.errSrc)` 其實(shí)是始終為真的,這也就導(dǎo)致了死循環(huán),會重復(fù)調(diào)用加載函數(shù)。
onerror = (evt) => {
// 判斷始終為真
if (this.props.errSrc) {
return this.loadCreateImg(this.props.errSrc)
}
// 否則返回失敗回調(diào)
this.emit('error', evt)
}
復(fù)制代碼

所以就需要給它一個(gè)可以變?yōu)榧俚臅r(shí)機(jī),那么修復(fù)方法為:在傳給 loadCreateImg 方法之后,將 errSrc 清空,這樣加載一次之后就可以判斷為假了,所以完整代碼為:

class Load {
constructor(node, props, emit) {
this.node = node
this.props = props
this.emit = emit
}
loadCreateImg = (errSrc?: string) => {
const newImg = new Image()
// 如果 errSrc 存在 就嘗試加載 errSrc
if (errSrc) {
newImg.src = errSrc
} else {
newImg.src = this.props.src
}
newImg.addEventListener('error', (evt) => {
this.onerror(evt)
})
newImg.addEventListener('load', (evt) => {
this.onload(evt, newImg.src) // 將加載成功的 src 傳給 onload 函數(shù)
})
}
// 新增 src 屬性
onload = (evt, src: string) => {
this.node.src = src // 將真實(shí) dom 的 src 賦值給傳入的 src
this.emit('load', evt) // 新增
}
onerror = (evt) => {
if (this.props.errSrc) {
this.loadCreateImg(this.props.errSrc)
this.props.errSrc = '' // 清空 errSrc 避免重復(fù)調(diào)用死循環(huán)
return
}
this.emit('error', evt)
}
}
復(fù)制代碼

回調(diào)函數(shù)

有些時(shí)候,我們還需要通過一個(gè)布爾值來判斷圖片是否加載成功,或者進(jìn)行其它判斷。

Fighting Design 內(nèi)部對圖片加載失敗做了特殊的樣式處理來提示用戶,所以需要一個(gè)布爾值和 v-if 來展示不同的狀態(tài),這里就涉及到了類的第四個(gè)參數(shù),也就是一個(gè)可選的回調(diào)函數(shù)

這樣就可以在加載成功和加載失敗的時(shí)候通過回調(diào)函數(shù)來返回一個(gè)布爾值判斷是否加載成功,代碼如下:

class Load {
constructor(node, props, emit, callback) {
this.node = node
this.props = props
this.emit = emit
this.callback = callback // 新增 callback 參數(shù)
}
loadCreateImg = (errSrc?: string) => {
const newImg = new Image()
if (errSrc) {
newImg.src = errSrc
} else {
newImg.src = this.props.src
}
newImg.addEventListener('error', (evt) => {
this.onerror(evt)
})
newImg.addEventListener('load', (evt) => {
this.onload(evt, newImg.src)
})
}
onload = (evt, src: string) => {
this.node.src = src
this.emit('load', evt)
// 如果 callback 存在,在加載成功的時(shí)候返回 true
if (this.callback) {
this.callback(true)
}
}
onerror = (evt) => {
if (this.props.errSrc) {
this.loadCreateImg(this.props.errSrc)
this.props.errSrc = ''
return
}
this.emit('error', evt)
// 如果 callback 存在,在加載失敗的時(shí)候返回 false
if (this.callback) {
this.callback(false)
}
}
}
復(fù)制代碼

上面代碼即可實(shí)現(xiàn)判斷是否加載成功的需求。

當(dāng)然,回調(diào)函數(shù)你可以盡情的發(fā)揮想象做出更多的事情,這里僅提供部分用法。

懶加載

圖片的懶加載,也是一個(gè)圖片加載必備的功能了,這里我使用的是內(nèi)置的 IntersectionObserver[5] 接口,對于這個(gè)方法,這里不過多描述,各位可以通過 MDN[6] 進(jìn)行學(xué)習(xí)。

對于懶加載,因?yàn)檫@是一個(gè)可選的屬性,并不是每次都需要,所以我將懶加載單獨(dú)抽離出來的一個(gè) Lazy 類進(jìn)行實(shí)現(xiàn),再將 Lazy 類繼承到 Load 類,代碼如下:

class Lazy extends Load {
constructor(img, props, emit, callback) {
// super 關(guān)鍵字調(diào)用
super(img, props, emit, callback)
}
observer = () => {
const observer = new IntersectionObserver(
(arr): void => {
// 如果進(jìn)入可視區(qū)域
if (arr[0].isIntersecting) {
// 開始加載圖片 調(diào)用父類
this.loadCreateImg()
observer.unobserve(this.node)
}
},
/**
* rootMargin 為觸發(fā)懶加載的距離 通過 props 傳入
* https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin
*/
{ rootMargin: this.props.rootMargin }
)
return observer
}
// 執(zhí)行 懶加載
lazyCreateImg = (): void => {
// IntersectionObserver 內(nèi)部方法,需要將 dom 節(jié)點(diǎn)傳入
this.observer().observe(this.node)
}
}
復(fù)制代碼

IntersectionObserver 接口可以判斷 dom 元素是否進(jìn)入可視區(qū)域,通過內(nèi)置方法判斷進(jìn)入可視區(qū)域之后,執(zhí)行父類的 loadCreateImg 方法進(jìn)行加載,從而實(shí)現(xiàn)懶加載。

對外接口

對于 Load 類和 Lazy 類,F(xiàn)ighting Design 并沒有直接暴露出去提供使用,而是暴露出了一個(gè)全新的 loadImage 函數(shù),讓它去根據(jù)是否為懶加載而實(shí)例化不同的類,再調(diào)用加載方法:

// 導(dǎo)出對外接口
export const loadImage = (node, prop, emit, callback) => {
/**
* 如果傳入了 lazy 則執(zhí)行懶加載類
* 否則執(zhí)行正常加載類
*/
if (prop.lazy) {
const lazy = new Lazy(node, prop, emit, callback)
return lazy.lazyCreateImg()
}
const load = new Load(node, prop, emit, callback)
load.loadCreateImg()
}
復(fù)制代碼

測試使用

寫好的函數(shù)測試一下看看:



復(fù)制代碼

可以看到,是成功執(zhí)行的。

完整代碼

完整代碼可參考 load-image[7]

寫在最后

這篇文章的圖片加載設(shè)計(jì),取自我?guī)ьI(lǐng)社區(qū)小伙伴一起做的開源 vue3 組件庫 Fighting Design[8] 中的部分源碼,想?yún)⑴c開源組件庫的開發(fā)也可以直接加我的微信:VirgoTyh 一起共同學(xué)習(xí)。

歡迎大家多多點(diǎn)贊 評論 Star,還有什么需要完善的可以給我評論留言。不要懼怕寫出不完美的代碼,只要在后續(xù)迭代過程中見招拆招,代碼就會變得越來越完善,框架也會變得越來越健壯。


新聞名稱:關(guān)于圖片加載,你需要學(xué)習(xí)一下
分享網(wǎng)址:http://www.dlmjj.cn/article/dhheshh.html