对于jQuery性能的一些优化建议


Posted in Javascript onAugust 13, 2015

不要每次都在循环中访问数组的 length 属性,应在循环开始之前就将其缓存:

var myLength = myArray.length;

for (var i = 0; i < myLength; i++) {
  // do stuff
}

在循环外执行 append 操作

直接操作 DOM 是非常耗费性能的,尤其不要在循环中直接操作 DOM:

// 这样性能很差
$.each(myArray, function(i, item) {
  var newListItem = '<li>' + item + '</li>';
  $('#ballers').append(newListItem);
});
// 这样性能较好
var frag = document.createDocumentFragment();
$.each(myArray, function(i, item) {
    var newListItem = '<li>' + item + '</li>';
    frag.appendChild(newListItem);
});
$('#ballers')[0].appendChild(frag);
// 这样也很好
var myHtml = '';
$.each(myArray, function(i, item) {
    html += '<li>' + item + '</li>';
});
$('#ballers').html(myHtml);

代码要保持精炼

避免做重复的事情。如果你一直在做重复的事情,那么就可能出问题了:

// 丑
if ($eventfade.data('currently') != 'showing') {
  $eventfade.stop();
}

if ($eventhover.data('currently') != 'showing') {
  $eventhover.stop();
}

if ($spans.data('currently') != 'showing') {
  $spans.stop();
}

// 漂亮!!
var $elems = [$eventfade, $eventhover, $spans];
$.each($elems, function(i,elem) {
  if (elem.data('currently') != 'showing') {
    elem.stop();
  }
});

警惕匿名函数

匿名函数满天飞是很痛苦的事情,它们难以调试、维护、测试或者复用,应尽可能的对函数命名并将其封装在对象中,实施有效的管理:

// 不好
$(document).ready(function() {
  $('#magic').click(function(e) {
    $('#yayeffects').slideUp(function() {
      // ...
    });
  });

  $('#happiness').load(url + ' #unicorns', function() {
    // ...
  });
});

// 好
var PI = {
  onReady : function() {
    $('#magic').click(PI.candyMtn);
    $('#happiness').load(PI.url + ' #unicorns', PI.unicornCb);
  },

  candyMtn : function(e) {
    $('#yayeffects').slideUp(PI.slideCb);
  },

  slideCb : function() { ... },

  unicornCb : function() { ... }
};

$(document).ready(PI.onReady);

选择器的优化

随着越来越多的浏览器支持 document.querySelectorAll(),选择器的重担已经慢慢转移给浏览器了,但还是有一些技巧需要注意:

优先并尽可能地使用 ID 选择器:

// 快
$('#container div.robotarm');

// 相当快
$('#container').find('div.robotarm');

使用 $.fn.find 的方式更快,因为在 $.fn.find 之前的选择器并没有使用 jQuery 自带的 Sizzle 选择器引擎,而是使用了浏览器 document.getElementById() 方法,浏览器原生的方法自然更快。

使用组合选择器时,尽可能使右端更明确,而左端不尽量不明确:

// 未优化
$('div.data .gonzalez');

// 已优化
$('.data td.gonzalez');

尽量在选择器右端使用 tag.class,而左端尽可能只使用 tag 或者 .class。

避免过度具体:

$('.data table.attendees td.gonzalez');

// 在不影响结果的情况下尽量删掉中间多余部分
$('.data td.gonzalez');

简洁的 DOM 结构也有助于提升选择器的性能,因为选择器可能少走几层弯路去寻找那些元素。

尽量避免使用通配符,任何显式或隐式的使用通配符,都会降低选择器的性能:

$('.buttons > *');    // 极慢
$('.buttons').children(); // 好多了

$('.gender :radio');   // 隐式地使用通配符,慢
$('.gender *:radio');   // 显式地使用通配符,同上,慢
$('.gender input:radio'); // 嗯,快多了

使用事件代理

事件代理允许将一个事件绑定到某个容器上(例如一个无序列表 ul),而不是绑定到容器内所有元素上(例如列表元素 li)。虽说 $.fn.live 和 $.fn.delegate 都是将事件绑定到容器上,但是应尽可能是用 $.fn.delegate,毕竟其明确的上下文(相较于 $.fn.live 的上下文是 document)要小得多,避免了很多不必要的过滤。

除了性能方面的提升,如果给绑定了事件的容器内添加新元素,那么这些新元素就无须再次绑定事件了,这也是个优点。

// 不好 (如果列表元素非常多,你就悲剧了)
$('li.trigger').click(handlerFn);

// 好些:使用 $.fn.live 进行事件代理
$('li.trigger').live('click', handlerFn);

// 最好:使用 $.fn.delegate 进行事件代理
// 因为这样可以明确的指定一个上下文
$('#myList').delegate('li.trigger', 'click', handlerFn);

将元素从 DOM 卸载出来再操作

DOM 操作是比较慢的,所以应尽量避免直接操作 DOM。jQuery 在其 1.4 版中引入了 $.fn.detach 方法,可以将元素从 DOM 中卸载出来然后进行操作,操作好了之后再添加到 DOM 中:

var $table = $('#myTable');
var $parent = $table.parent();

$table.detach();
// ... 例如这里给表格添加了很多很多行
$parent.append(table);

使用外部样式表为大量元素修改样式

当使用 $.fn.css 为超过 20 个元素修改样式时,应考虑直接在页面中添加 style 标签,据说性能可提升 60%。

// 当元素少于 20 个时使用这个方法,多余 20 个时,速度就慢了
$('a.swedberg').css('color', '#asd123');

// 多余 20 个元素时,应考虑直接在页面中添加 style 标签
$('<style type="text/css">a.swedberg { color : #asd123 }</style>')
  .appendTo('head');

使用 $.data 替代 $.fn.data

将 $.data 应用于 DOM 元素上,比直接在选择器上调用 $.fn.data 要快 10 倍。当然,前提是要先明白 DOM 元素和 jQuery 结果集之间的区别。

// 速度一般
$(elem).data(key,value);

// 速度提升 10 倍
$.data(elem,key,value);

别在空元素上浪费时间

jQuery 不会主动告诉你,你正在一个空白的结果集上运行代码 ? 而且执行过程中并未出错。所以有时候再执行代码之前,需要先判断一下结果集是否为空:

// 不好:执行了三个函数之后
// 才发现结果集上没有任何元素
$('#nosuchthing').slideUp();

// 好
var $mySelection = $('#nosuchthing');
if ($mySelection.length) { $mySelection.slideUp(); }

// 最好:增加一个 doOnce 插件
jQuery.fn.doOnce = function(func){
  this.length && func.apply(this);
  return this;
}

$('li.cartitems').doOnce(function(){
  // 这里可以确保结果集不是空的
});

这种方法特别适用于 jQuery UI 方面,因为即使结果集中不包含任何元素,其开销也会很大。
变量的定义

可以在一条语句中定义多个变量:

// 老掉牙的写法
var test = 1;
var test2 = function() { ... };
var test3 = test2(test);

// 新写法
var test = 1,
  test2 = function() { ... },
  test3 = test2(test);

在自执行函数中,变量甚至可以不用定义:

(function(foo, bar) { ... })(1, 2);

条件判断

// 土方法
if (type == 'foo' || type == 'bar') { ... }

// 较先进的方法
if (/^(foo|bar)$/.test(type)) { ... }

// 使用对象查找
if (({ foo : 1, bar : 1 })[type]) { ... }
Javascript 相关文章推荐
JQuery实现自定义对话框的代码
Jun 15 Javascript
Extjs4 GridPanel 的几种样式使用介绍
Apr 18 Javascript
jQuery过滤选择器用法分析
Feb 10 Javascript
浅谈Javascript实现继承的方法
Jul 06 Javascript
jQuery网页定位导航特效实现方法
Dec 19 Javascript
JS给按钮添加跳转功能类似a标签
May 30 Javascript
Mongoose实现虚拟字段查询的方法详解
Aug 15 Javascript
js使用xml数据载体实现城市省份二级联动效果
Nov 08 Javascript
vue项目设置scrollTop不起作用(总结)
Dec 21 Javascript
vuex 实现getter值赋值给vue组件里的data示例
Nov 05 Javascript
详解为element-ui的Select和Cascader添加弹层底部操作按钮
Feb 07 Javascript
Node.js API详解之 dgram模块用法实例分析
Jun 05 Javascript
使用控制台破解百小度一个月只准改一次名字
Aug 13 #Javascript
js实现文本框宽度自适应文本宽度的方法
Aug 13 #Javascript
理解和运用JavaScript的闭包机制
Aug 13 #Javascript
详解JavaScript中jQuery和Ajax以及JSONP的联合使用
Aug 13 #Javascript
JavaScript的面向对象编程基础
Aug 13 #Javascript
JavaScript简单判断复选框是否选中及取出值的方法
Aug 13 #Javascript
JavaScript实现将文本框的值插入指定位置的方法
Aug 13 #Javascript
You might like
用PHP生成静态HTML速度快类库
2007/03/18 PHP
php whois查询API制作方法
2011/06/23 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十六)
2014/06/30 PHP
yii2 RBAC使用DbManager实现后台权限判断的方法
2016/07/23 PHP
关于ThinkPhp 框架表单验证及ajax验证问题
2017/07/19 PHP
PHPExcel 修改已存在Excel的方法
2018/05/03 PHP
PHP常用日期加减计算方法实例小结
2018/07/31 PHP
php7 list()、session及其他模块的修改实例分析
2020/05/25 PHP
给网站上的广告“加速”显示的方法
2007/04/08 Javascript
学习ExtJS(一) 之基础前提
2009/10/07 Javascript
javascript测试题练习代码
2012/10/10 Javascript
js中页面的重新加载(当前页面/上级页面)及frame或iframe元素引用介绍
2013/01/24 Javascript
node.js中的events.emitter.once方法使用说明
2014/12/10 Javascript
JavaScript实现对下拉列表值进行排序的方法
2015/07/15 Javascript
JS实现动态生成表格并提交表格数据向后端
2020/11/25 Javascript
使用getBoundingClientRect方法实现简洁的sticky组件的方法
2016/03/22 Javascript
基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍
2016/05/12 Javascript
jQuery实现打开页面渐现效果示例
2016/07/27 Javascript
在react-router4中进行代码拆分的方法(基于webpack)
2018/03/08 Javascript
vue实现点击选中,其他的不选中方法
2018/09/05 Javascript
React router动态加载组件之适配器模式的应用详解
2018/09/12 Javascript
JavaScript解析JSON数据示例
2019/07/16 Javascript
layui插件表单验证提交触发提交的例子
2019/09/09 Javascript
[08:42]DOTA2每周TOP10 精彩击杀集锦vol.2
2014/06/25 DOTA
python Flask实现restful api service
2017/12/04 Python
python中从str中提取元素到list以及将list转换为str的方法
2018/06/26 Python
python模块和包的应用BASE_PATH使用解析
2019/12/14 Python
英国最大的婴儿监视器网上商店:Baby Monitors Direct
2018/04/24 全球购物
OSPF有什么优点?为什么OSPF比RIP收敛快?
2013/02/13 面试题
大学四年规划书范文
2013/12/27 职场文书
企业内部培训方案
2014/02/04 职场文书
幼儿园家长评语大全
2014/04/16 职场文书
学生会感恩节活动方案
2014/10/11 职场文书
领导干部个人整改措施落实情况汇报
2014/10/29 职场文书
导游欢送词
2015/01/31 职场文书
解决IDEA翻译插件Translation报错更新TTK失败不能使用
2022/04/24 Python