JavaScript 代理模式
以瀏覽器載入圖片為例子,當圖片完全載入後才會出現在螢幕上,但是等待載入的這段時間會使得原本該出現圖片的地方為空白的狀態,一旦圖片載入完成出現時,會造成畫面元件跳動的情況。解決的方式有以下兩個方式: 1. 使用 loading 圖示顯示正在載入,圖片載入完成後取代 loading 圖示。2. 使用同等比例的小圖片當成佔位符,圖片載入完成後取代此佔位符。
1 2 3 4
| const myImage = new Image(); myImage.src = 'https://source.unsplash.com/2560x1600/?nature,water' document.body.appendChild(myImage);
|
上述為一般圖片載入情況,在圖片載入完成後,會發現畫面元件跳動的情況。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const createImg = function(src) { const myImage = new Image() myImage.src = src document.body.appendChild(myImage) } const proxyImg = (function() { const realImg = new Image() realImg.onload = function() { createImg(this.src) } return function(src) { createImg('https://m.popkey.co/163fce/Llgbv_s-200x150.gif') realImg.src = src } })() proxyImg('https://source.unsplash.com/2560x1600/?nature,water')
|
上述為我在撰寫階段犯的一個錯誤,我們最後需要的是圖片載入完成後,將 myImage
的 src
從 gif url 替換成 image url 。
問題在於 createImg
每次執行都會產生一個新的 image
物件,因此儘管 proxyImg
幫我們完成置換 url 的目的,但是最後會生成兩張圖片,不符合預期結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const createImg = (function(src) { const myImage = new Image() document.body.appendChild(myImage) return { setSrc(src){ myImage.src =src } } })() const proxyImg = (function() { const realImg = new Image() realImg.onload = function() { createImg.setSrc(this.src) } return { setSrc(src) { createImg.setSrc('https://m.popkey.co/163fce/Llgbv_s-200x150.gif') realImg.src = src } } })() proxyImg.setSrc('https://source.unsplash.com/2560x1600/?nature,water')
|
上述為使用 loading 圖示作為代理的正確例子。由於上個例子 createImg
每次都會創立新的 Image
物件,因此這裡使用 IIFE 確保只會有一個 Image
物件產生,並釋出 setSrc
的介面供外界設定圖片的 url。
proxyImg
也做了一些修改,為了確保與 createImg
介面一致,因此將介面修改成具有setSrc
的方法供外界設定圖片的 url。
代理的重點在於先讓圖片執行載入(const realImg = new Image(); realImg.src = src
),直到圖片載入完成後才將 url 替換掉(realImg.onload = function() {createImg.setSrc(this.src)}
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const createImg = (function(src) { const myImage = new Image() document.body.appendChild(myImage) return { setSrc(src){ myImage.src =src } } })() const proxyImg = (function() { const realImg = new Image() realImg.onload = function() { createImg.setSrc(this.src) } return { setSrc(src) { createImg.setSrc('https://placem.at/places?w=80&h=50&txt=loading&overlay_color=eee') realImg.src = src } } })() proxyImg.setSrc('https://source.unsplash.com/2560x1600/?nature,water')
|
上述為使用站位符 (placeholder) 作為代理的正確例子。與上個例子稍有不同的地方在於代理圖片與原圖片等比例,但是代理圖片小很多,因此載入完成的時間極短,如果在 CSS
讓代理圖片與原圖片的寬相同,則會形成一個佔住相同大小位置的圖片,一但原圖片載入完後,則將 url 從代理圖片置換成原圖片,因此不會看到畫面有跳動的情形。