移动端性能优化(JavaScript性能优化)
程序员文章站
2022-06-19 10:33:41
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>¥109.90</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">89.9</strong></span> <span class="recommend-count">6294件已售</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/7.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">tp-link 全千兆端口双频无线路由器</p> <p class="recommend-origprice"> <del>¥179.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">169</strong></span> <span class="recommend-count">4255件已售</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/8.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">【前1800名再减50】家用高压洗车机</p> <p class="recommend-origprice"> <del>¥790.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">268</strong></span> <span class="recommend-count">1599件已售</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/9.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">德国鲁茜rusch迷你婴儿辅食机 宝宝</p> <p class="recommend-origprice"> <del>¥898.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">159</strong></span> <span class="recommend-count">881件已售</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/10.jpg" alt="recommend" class="recommend-img lazyload-img"> </p> <p class="recommend-name">西域之尚红枣500g*5袋</p> <p class="recommend-origprice"> <del>¥89.00</del> </p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">29.9</strong></span> <span class="recommend-count">15049件已售</span> </p> </a> </li> </ul> </div> <!-- 资源按需加载 --> <div id="product" class="product" style="height: 500px; background-color: red; text-align: center; line-height: 500px; font-size: 2.5rem; color: #fff;"> <img src="img/loading.gif" alt=""> </div> </div> <div class="tabbar-container"> <ul class="tabbar"> <li class="tabbar-item tabbar-item-active"> <a href="###" class="tabbar-link"> <i class="iconfont icon-home"></i> <span>首页</span> </a> </li> <li class="tabbar-item"> <a href="###" class="tabbar-link"> <i class="iconfont icon-category"></i> <span>分类页</span> </a> </li> <li class="tabbar-item"> <a href="###" class="tabbar-link"> <i class="iconfont icon-cart"></i> <span>购物车</span> </a> </li> <li class="tabbar-item"> <a href="###" class="tabbar-link"> <i class="iconfont icon-personal"></i> <span>个人中心</span> </a> </li> </ul> </div> <script> // 1. 图片的按需加载 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 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--;//数组长度减少 } } } // 是否在页面可视区内 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; } </script> </body> </html>
其他资源的按需加载
js/loadproduct.js
(function () { var product = document.getelementbyid('product'); product.innerhtml = '我是按需加载的'; })();
html中代码添加:
// 2. 其他内容的按需加载 loadproduct(); window.addeventlistener('scroll', loadproduct, false); function loadproduct() { if (isinvisiblearea(document.getelementbyid('product'))) { var script = document.createelement('script'); // script.src = 'js/loadproduct.js'; //使用settimeout是为了模拟网络延迟,上线时不需要延迟 settimeout(function () { script.src = 'js/loadproduct.js'; }, 1000); document.body.appendchild(script);//载入loadproduct.js window.removeeventlistener('scroll', loadproduct, false); } }
预加载(空闲的时候加载,之后用到的时候已经加载完了)
常用于浏览漫画的时候
// 3. 图片预加载 var img = new image(); img.src = 'img/recommend/5.jpg';
(还没滚动就已经预加载了)