JavaScript性能優化 DOM操作優化 查找DOM元素能用id就用id,是最高效的查找方法(還得看具體環境) document.getElementById('slider') 從document開始查找子元素效率不高,建議從具體父元素開始查找 減少元素查找的層級 var sliderItem ...
JavaScript性能優化
DOM操作優化
查找DOM元素能用id就用id,是最高效的查找方法(還得看具體環境)
document.getElementById('slider')
從document開始查找子元素效率不高,建議從具體父元素開始查找
減少元素查找的層級
var sliderItemContainer = document.querySelector('.slider-item-container');//從DOM開始查找 var sliderItemContainer = sliderEl.querySelector('.slider-item-container');//從具體元素開始查找
DOM操作完使用變數緩存,否則需要多次操作DOM就得多次獲取
// 合理緩存DOM對象 var sliderItemContainer = sliderEl.querySelector('.slider-item-container'), sliderItem = sliderItemContainer.querySelectorAll('.slider-item'), indicatorContainer = document.createElement('div');
在迴圈中操作字元串,最後再操作DOM;比每次迴圈操作DOM要好得多
不推薦
for (var i = 0, num = sliderItem.length; i < num; i++) { indicatorContainer.innerHTML += '<span class="slid }
推薦
// 減少操作DOM的次數 var html=""; for (var i = 0, num = sliderItem.length; i < num; i++) { html += '<span class="slider-indicator"></span>'; } indicatorContainer.innerHTML=html ; }
也可以使用文檔碎片,因為還沒有被渲染呈現,因此迴圈中不涉及DOM渲染
在迴圈外只創建一遍文檔碎片節點,在迴圈內克隆節點即可,傳入true可以帶內容一起拷貝
var indicatorItemFragment = document.createDocumentFragment();//創建文檔片段 var spanEl = document.createElement('span'); // 減少操作DOM的次數 for (var i = 0, num = sliderItem.length; i < num; i++) { var indicatorItem = spanEl.cloneNode(true); indicatorItem.className = 'slider-indicator'; indicatorItemFragment.appendChild(indicatorItem); }
迴圈的長度也用變數保存下來,只在最初執行一次
不推薦
for (var i = 0; i < sliderItem.length; i++)
推薦
for (var i = 0, num = sliderItem.length; i < num; i++)
不要直接修改style,通過添加class修改 直接修改style,如果修改的內容多,需要多次操作DOM;另外會涉及重排和重繪 重排涉及元素的位置關係變化,重繪涉及到元素的顏色透明度等 重繪影響性能,重排比重繪更耗性能,因此需要減少重繪,避免重排 不推薦
// 不要直接修改style,通過添加class修改 if (i === activeIndex) { indicatorItem.style.backgroundColor = '#007aff'; indicatorItem.style.opacity = 1; // 重排 重繪 }
推薦
// 不要直接修改style,通過添加class修改 if (i === activeIndex) { indicatorItem.className += ' slider-indicator-active'; }
事件的優化:
避免事件多次綁定,而且如果改變節點時還需要增加或解除綁定
避免遍歷節點,給每個元素都綁定事件
可以使用事件代理或者事件委托,給它們的父元素綁定一次事件即可(事件冒泡原理)
不推薦
var sliderEl = document.getElementById('slider'), sliderIndicatorContainer = sliderEl.querySelector('.slider-indicator-container'), sliderIndicators = sliderIndicatorContainer.querySelectorAll('.slider-indicator'); // 事件綁定 for (var i = 0, num = sliderIndicators.length; i < num; i++) { sliderIndicators[i].addEventListener('click', function () { console.log('click'); }, false); } // 動態插入一個新節點 var sliderIndicator = document.createElement('span'); sliderIndicator.className = 'slider-indicator'; sliderIndicatorContainer.appendChild(sliderIndicator); sliderIndicator.addEventListener('click', function () { console.log('click'); }, false);
推薦
// 使用事件代理,避免直接事件綁定 // jQuery/Zepto $(sliderIndicatorContainer).on('click', '.slider-indicator', function () {}); //原生js sliderIndicatorContainer.addEventListener('click', function (ev) { // console.log(ev.target);//ev.target獲取到當前點擊的元素 if (ev.target && /(^|\s)slider\-indicator($|\s)/.test(ev.target.className)) { //正則判斷當前點擊的元素的className console.log('click'); } }, false);
針對執行非常頻繁的事件,使用事件節流來稀釋
比如 scroll resize mousemove touchmove等事件
// 事件節流 // scroll resize mousemove touchmove var timer = null; window.addEventListener('scroll', function () { console.log('scroll');//不節流 //100ms內有重覆執行,則取消前面的執行 clearTimeout(timer); timer = setTimeout(function () { console.log('scroll');//節流 }, 100); // .... }, false);
資源按需載入(懶載入,延遲載入)
將img的src屬性可以先放在data-src(自定義屬性)里,預設src里放一個統一的loading小圖標
給需要按需載入的圖片添加一個統一的類
<img src="img/loading.gif" data-src="img/recommend/1.jpg" alt="recommend" class="recommend-img lazyload-img">
獲取到所有需要按需載入的圖片節點,存入數組中
var lazyLoadClass = '.lazyload-img'; //Array.prototype.slice.call()類數組轉數組 var imgArr = Array.prototype.slice.call(document.querySelectorAll(lazyLoadClass));//獲取到所有需要按需載入的節點列表,轉數組 console.log(imgArr);
初始化時和每次滾動時,都執行按需載入函數
lazyLoadImgs();//初始化時執行圖片載入 //每次滾動時也執行圖片載入 var timer = null; window.addEventListener('scroll', function () { clearTimeout(timer); timer = setTimeout(function () { lazyLoadImgs(); }, 100); }, false);
判斷元素是否在可視區內
// 是否在頁面可視區內 function isInVisibleArea(el) { var rect = el.getBoundingClientRect(); // rect.top 可視區頂部到物體頂部 // rect.bottom 可視區底部到物體底部 // rect.right 可視區右邊到物體右邊 // rect.left 可視區左邊到物體左邊 return rect.bottom > 0 && rect.top < window.innerHeight && rect.right > 0 && rect.left < window.innerWidth; }
按需載入函數
function lazyLoadImgs() { for (var i = 0; i < imgArr.length; i++) { //判斷是否在可視區範圍 if (isInVisibleArea(imgArr[i])) { imgArr[i].src = imgArr[i].getAttribute('data-src');//用data-src屬性填充到src屬性中 imgArr.splice(i, 1);//數組中剔除已經載入的圖片 i--;//數組長度減少 } } }
完整代碼:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> <title>4.3 資源按需載入和預載入</title> <link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="css/icons.css"> <link rel="stylesheet" href="css/index.css"> <script src="js/flexible.js"></script> </head> <body> <header class="header-container"> <div class="navbar"> <div class="navbar-left"> <i class="iconfont icon-scan"></i> </div> <div class="navbar-center"> <div class="searchBox"> <div class="searchBox-prepend"> <i class="iconfont icon-search"></i> </div> <input type="text" placeholder="開學季有禮,好貨5折起" class="searchBox-input"> <div class="searchBox-append"> <i class="iconfont icon-close"></i> </div> </div> </div> <div class="navbar-right"> <i class="iconfont icon-msg"></i> </div> </div> </header> <div class="main-container"> <div class="slider-container"> <img src="img/slider/1.jpg" alt="slider"> </div> <nav class="nav-container"> <ul class="nav"> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/1.png" alt="nav" class="nav-img"> <span class="nav-text">團購</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/2.png" alt="nav" class="nav-img"> <span class="nav-text">一元購</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/3.png" alt="nav" class="nav-img"> <span class="nav-text">優惠券</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/4.png" alt="nav" class="nav-img"> <span class="nav-text">教育</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/5.png" alt="nav" class="nav-img"> <span class="nav-text">旅行</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/6.png" alt="nav" class="nav-img"> <span class="nav-text">線上訂餐</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/7.png" alt="nav" class="nav-img"> <span class="nav-text">慶典</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/8.png" alt="nav" class="nav-img"> <span class="nav-text">秒殺</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/9.png" alt="nav" class="nav-img"> <span class="nav-text">拍賣</span> </a> </li> <li class="nav-item"> <a href="###" class="nav-link"> <img src="img/nav/10.png" alt="nav" class="nav-img"> <span class="nav-text">服務</span> </a> </li> </ul> </nav> <div class="recommend-container"> <ul class="recommend"> <li class="recommend-item"> <a href="###" class="recommend-link"> <p class="recommend-pic"> <img src="img/loading.gif" data-src="img/recommend/1.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">歐派整體櫥櫃定製簡約現代</p> <p class="recommend-origPrice"> <del>¥2000.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">1000</strong></span> <span class="recommend-count">985件已售</span> </p> </a> </li> <li class="recommend-item"> <a href="###" class="recommend-link"> <p class="recommend-pic"> <img src="img/loading.gif" data-src="img/recommend/2.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">創維55吋4K超高清HDR</p> <p class="recommend-origPrice"> <del>¥2999.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">2299</strong></span> <span class="recommend-count">63件已售</span> </p> </a> </li> <li class="recommend-item"> <a href="###" class="recommend-link"> <p class="recommend-pic"> <img src="img/loading.gif" data-src="img/recommend/3.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">【到手259元】蘇泊爾 5L電壓力鍋</p> <p class="recommend-origPrice"> <del>¥799.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">299</strong></span> <span class="recommend-count">1908件已售</span> </p> </a> </li> <li class="recommend-item"> <a href="###" class="recommend-link"> <p class="recommend-pic"> <img src="img/loading.gif" data-src="img/recommend/4.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">三隻松鼠堅果禮包</p> <p class="recommend-origPrice"> <del>¥125.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">108</strong></span> <span class="recommend-count">9532件已售</span> </p> </a> </li> <li class="recommend-item"> <a href="###" class="recommend-link"> <p class="recommend-pic"> <img src="img/loading.gif" data-src="img/recommend/5.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">藍月亮洗衣液12斤</p> <p class="recommend-origPrice"> <del>¥133.40</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">89.9</strong></span> <span class="recommend-count">5399件已售</span> </p> </a> </li> <li class="recommend-item"> <a href="###" class="recommend-link"> <p class="recommend-pic"> <img src="img/loading.gif" data-src="img/recommend/6.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">福臨門葵花玉米油</p> <p class="recommend-origPrice"> <del