原生JS實現圖片懶載入(考慮不重複載入以及節流)
阿新 • • 發佈:2021-06-22
1.原生JS實現圖片懶載入(考慮不重複載入以及節流)
知識點:視口位置判斷,懶載入實現(data-set),節流等
1.Element.getBoundingClientRect()
該方法返回元素的大小及其相對於視口的位置,
具體解釋及用法參考 MDN.
通過 Element.getBoundingClientRect().top 與 window.innerHeight(當前視窗的高度)比較就可以判斷圖片是否出現在可視區域。
注意這個 top 是相對於當前視窗的頂部的top值而不是一開始的頂部。
2.通過 Element.dataset 可以獲取到為元素節點新增的data-*
屬性,我們可以通過這個屬性來儲存圖片載入時的url。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> * { padding: 0; margin: 0; } img { display: inline-block; width: 100%; height: 300px; background: gray; } </style> </head> <body> <div class="box-image"> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/252339290/TB29PYUXnIlyKJjSZFrXXXn2VXa_!!252339290-0-beehive-scenes.jpg_180x180xzq90.jpg_.webp" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i4/2260152888/O1CN01vw2e251XCkJr0VPZU_!!2260152888-0-beehive-scenes.jpg_250x250xz.jpg" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i3/24687421/TB2Xg1izsyYBuNkSnfoXXcWgVXa_!!24687421-0-beehive-scenes.jpg_250x250xz.jpg" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i4/890151842/TB2II1mnZbI8KJjy1zdXXbe1VXa_!!890151842-0-beehive-scenes.jpg_250x250xz.jpg" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/2096513830/TB2l1W0kRnTBKNjSZPfXXbf1XXa_!!2096513830-0-beehive-scenes.jpg_250x250xz.jpg" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i3/2586222636/TB2RDGrpqAoBKNjSZSyXXaHAVXa_!!2586222636-0-daren.jpg_250x250xz.jpg" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/1870112525/TB2Ae.xbOwIL1JjSZFsXXcXFFXa_!!1870112525-2-beehive-scenes.png_250x250xz.jpg" alt=""> <img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/2194952831/TB2PwWty7qvpuFjSZFhXXaOgXXa_!!2194952831-0-beehive-scenes.jpg_250x250xz.jpg" alt=""> </div> <script> var viewHeight = document.documentElement.clientHeight; // = window.innerHeight? // 節流:加一個300ms的間隔執行 function throttle(fn, wait) { let canRun = true return function (...args) { if (!canRun) return canRun = false setTimeout(() => { fn.apply(this, args) canRun = true }, wait) } } function lazyload() { let imgs = document.querySelectorAll('img[data-original][lazyload]') // 獲取文件中所有擁有data-original lazyload屬性的<img>節點 imgs.forEach(item => { if (item.dataset.original == '') {// HTMLElement.dataset屬性允許無論是在讀取模式和寫入模式下訪問在 HTML或 DOM中的元素上設定的所有自定義資料屬性(data-*)集。 return } // 返回一個DOMRect物件,包含了一組用於描述邊框的只讀屬性——left、top、right和bottom, // 單位為畫素。除了 width 和 height 外的屬性都是相對於視口的左上角位置而言的。 let rect = item.getBoundingClientRect() // 其top值是相對於當前視窗的頂部而言的而不是絕對的頂部,所以top值 < window.innerHeight的話圖片就出現在底部了就需要載入 if (rect.bottom >= 0 && rect.top < viewHeight) { let img = new Image() img.src = item.dataset.original // 圖片載入完成觸發load事件? img.onload = function () { item.src = img.src } // 移除屬性的話就不會重複載入了 item.removeAttribute('data-original') item.removeAttribute('lazyload') } }) } // 先呼叫一次載入最初顯示在視窗中的圖片 lazyload(); let throttle_lazyload = throttle(lazyload, 300) document.addEventListener('scroll', throttle_lazyload) </script> </body> </html>
2.如何渲染幾萬條資料且不卡住頁面?
①利用文件碎片,實現一次性插入多個節點,減少迴流
②分批次地插入節點而不是一次性插入,防止阻塞
③使用requestAnimationFrame讓瀏覽器選擇最為合適的渲染間隔
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <ul> 控制元件 </ul> <script> const total = 100000 // 10萬條資料 const once = 20 // 每輪插入的資料條目 const loopCount = total / once // 渲染總次數 let countOfRender = 0 let ul = document.querySelector('ul') function add() { // 使用文件碎片優化效能 const fragment = document.createDocumentFragment() for (let i = 0; i < once; i++) { const li = document.createElement('li') li.innerText = Math.floor(Math.random() * total) fragment.appendChild(li) } ul.appendChild(fragment) countOfRender+=1 loop() } function loop() { if (countOfRender < loopCount) { window.requestAnimationFrame(add) // 使用requestAnimationFrame每隔16ms(瀏覽器自己選擇最佳時間)重新整理一次 } } </script> </body> </html>
3.寫函式實現任意標籤轉換成json形式
/*
<div>
<span>
<a></a>
</span>
<span>
<a></a>
<a></a>
</span>
</div>
*/
function DOM2JSON(str) {
let reg = /<(.+)>(.*?)<\/\1>/g
let result = null
let nodes = []
while((result = reg.exec(str))!== null) {
nodes.push({ tag: result[1], children: DOM2JSON(result[2])}) // exec返回的陣列,[0]匹配的字串 然後依次是捕獲的分組 然後有index和input屬性
}
return nodes.length > 1 ? nodes : nodes[0]
}
console.log(JSON.stringify(DOM2JSON('<div><span><a></a></span><span><a></a><a></a></span></div>')))
// {"tag":"div","children":[{"tag":"span","children":{"tag":"a"}},{"tag":"span","children":[{"tag":"a"},{"tag":"a"}]}]}
這裡主要利用了 exec 函式會在上一次匹配的結果之後繼續匹配,且如果未匹配成功會返回 null,然後注意下 exec 和正則表示式分組的使用即可。
4.判斷執行順序(事件迴圈)
console.log('begin'); // 1.begin
setTimeout(() => {
console.log('setTimeout 1'); // 3.setTimeout 1
Promise.resolve() // Promise.resolve().then ?
.then(() => {
console.log('promise 1'); // 5.promise 1
setTimeout(() => {
console.log('setTimeout2'); // 8. setTimeout2
});
})
.then(() => {
console.log('promise 2'); // 7. promise 2 注意7比8要快,會同時放入巨集任務和微任務佇列?
});
new Promise(resolve => {
console.log('a'); // 4. a
resolve();
}).then(() => {
console.log('b'); // 6. b
});
}, 0);
console.log('end'); // 2.end
5.實現一個 sleep 函式
async function test () {
console.log('start')
await sleep(3000)
console.log('3s has passed')
}
function sleep (ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, ms)
})
}
6.meta 標籤的作用
meta 標籤分兩大部分:HTTP 標題資訊(http-equiv)和頁面描述資訊(name)。
1、宣告文件使用的字元編碼
<meta charset='utf-8'>
以下設定更為詳細:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
該 meta 標籤定義了 HTML 頁面所使用的字符集為 utf-8 ,就是萬國碼。它可以在同一頁面顯示中文簡體、繁體及其它語言(如日文,韓文)等
2、宣告文件的相容模式
// 指示IE以目前可用的最高模式顯示內容
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="X-UA-Compatible" content="IE=Emulate IE7" />
// 指示IE使用 <!DOCTYPE> 指令確定如何呈現內容。標準模式指令以IE7 標準模式顯示,而 Quirks 模式指令以 IE5 模式顯示
3、SEO 優化
<meta name="description" content="不超過150個字元" /> // 頁面描述
<meta name="keywords" content="html5, css3, 關鍵字"/> // 頁面關鍵詞
<meta name="author" content="魔法小棧" /> // 定義網頁作者
// 定義網頁搜尋引擎索引方式,robotterms是一組使用英文逗號「,」分割的值,通常有如下幾種取值:none,noindex,nofollow,all,index和follow。
<meta name="robots" content="index,follow" />
4、為移動裝置新增 viewport
<meta name ="viewport" content ="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
/*
content 引數解釋:
width viewport 寬度(數值/device-width)
height viewport 高度(數值/device-height)
initial-scale 初始縮放比例
maximum-scale 最大縮放比例
minimum-scale 最小縮放比例
user-scalable 是否允許使用者縮放(yes/no)
minimal-ui iOS 7.1 beta 2 中新增屬性,可以在頁面載入時最小化上下狀態列。這是一個布林值,可以直接這樣寫:
*/
<meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui">
例如如下設定:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
- 強制讓文件與裝置的寬度保持 1:1 ;
- 文件最大的寬度比列是1.0( initial-scale 初始刻度值和 maximum-scale 最大刻度值);
- user-scalable 定義使用者是否可以手動縮放( no 為不縮放),使頁面固定裝置上面的大小;
更多內容參考 https://blog.csdn.net/xmtblog/article/details/46226717