在公司有一个需求
优化视频播放器
播放器菜单是由JS 动态创建,有大量的Dom 创建操作
由于之前没有考虑到这种大数据量的情况下,在1000条数据 甚至连IE8都没有办法正常使用。
有两个菜单一个是
table 全列表,将所有的数据全部展现
一个是树形菜单,有分级(理论上无限级)
1. 不要使用 $("
在大量的拼接之下,效率还是innerHTML 来的更高
http://www.cnblogs.com/jazzka702/archive/2009/05/23/1487789.html
http://hi.baidu.com/xu4820811cs/blog/item/365010ae705f62f3fbed50c8.html
网上已经有很多对比,这里就不赘述了。
当然 不能用 div.innerHTML += ""; 如果这样。 还不如CreatElement
通过字符串拼接 将所有的你想要的 HTML 拼接完成后,统一 innerHTML 创建.
2.说到字符串拼接,吐槽的点又来了。
你在网上搜索 往往都推荐你用数组拼接字符串,说速度要快一点 var arr = []; arr.push(""); arr.join('');
其实不然。
IE7,8 数组拼接 和 string+string 其实速度差异不大
IE9,ff,google,opera 下 string+string 都明显强于 arr
说穿了 还是为了照顾IE6
同样的拼接字符串 在IE6下。 数组还过得去, string+string 直接假死半天
3.尽量使用大容器控制事件
在之前的代码中 在使用$("
其实在大量数据情况下,这也是不小的消耗。
比如
a | text | A | A | |
a | text | A | A | |
a | text | A | A | |
a | text | A | A | |
a | text | A | A |
我要在第一列和第三列的A标签 加事件
那么就应该在整个Table上面Click 事件
而不是在创建的时候给每一个A标签加上事件.
table 的 click的事件捕捉到后判断是否是A标签。 然后去做相应事件。
当然这种做法 还是有他的缺点。 不方面传值。 往往只能通过A标签 TR TD 上的属性来传值
4.SetTimeOut 异步输出。
其实在做完上面3的工作之后,除了IE6 已经完全是秒开了。
但是IE6还是有明显的卡顿。
SetTimeOut(function() = aaa
{
//详细输出代码
SetTimeOut(aaa,time)
},time);
判断好一次最大输出值,输出多少次。
这样的效果非常好。 在固定的一屏宽高下。 比如你一次输出100行正好可以铺满。
用户甚至感觉不到延时。
做完上面4部 在2000行 在IE6下面也毫无压力。
可惜在测试部传来噩耗。
我发现我忽略了一个问题。
在大量分组下,不太好使用SetTimeOut。而且遍历树的时候消耗极大
1.因为分组很琐碎,无法像整个输出一样 分割来输出(ps:其实这个都很好办,硬输出IE6 还是吃的下,在实在不行的情况下 还是可以SetTimeOut 分组输出)
2.遍历树 问题来了
{
{ id:1,pid:0}
{ id:2,pid:1}
{ id:3,pid:2}
{ id:4,pid:3}
}
类似于这样的数据(理论上是无限层)
原来的方法,也就是最正统的方法。
递归遍历
先遍历PID=0
然后再通过结果一层一层的继续遍历。 在层数深且多的情况下。IE6直接 禁止脚本。
想到了两种办法。
1. 采用101010101 这种方式。
ID自带节点信息。可以顺序遍历 我可以一次遍历就可以输出树
但是直接被boss pass掉。。 难道重新做数据库啊
当然这种写法也有他的问题
(1)首先不好支持无限层
(2)当移动节点的时候。 恶心事儿就来了
2.JS端 重新整理
换句话说 当Json 格式数据传到客户端后。
通过1-2次遍历重新整理为我想要的格式
{
{ id:1,pid:0,child:{
{id:2,pid:1}
}}
}
1 var newGroup = {}; 2 var errerGroup = {}; 3 function Deep(json) 4 { 5 var arr,g; 6 for(var i in json) 7 { 8 g = json[i]; 9 arr = FindNodes(g);
SetNodes(g,arr); 10 } 11 } 12 13 function FindNodes(g,arr) 14 { 15 if(!arr) arr = []; 16 if(g.pid == 0){ //顶级分类 17 return arr; 18 } 19 else { 20 var pGroup = newGroup[g.pid]; 21 if(!pGroup) { 22 errerGroup[g.pid] = g; 23 return; 24 } 25 else { 26 arr.push(g.pid); 27 return FindNodes(pGroup,arr); 28 } 29 } 30 } 31 32 function SetNodes(g,arr) 33 { 34 if(!arr || arr.length == 0) 35 { 36 return newGroup[g.id] = g; 37 } 38 else 39 { 40 var tmpGroup = newGroup; 41 for(var i in arr) 42 { 43 tmpGroup = tmpGroup[arr[i]]; 44 } 45 tmpGroup[g.id] = g; 46 newGroup[g.id] = { pid:g.pid }; 47 } 48 }
这样newGroup 却是可以得到我想要的分组信息
但是有2个问题
1. 这样的写法 会改变原来的 json
当你得到 新的Json 的时候
你会发现 你传入的Json 已经被改变.
如果是克隆一份 (PS: 还没有测试), 但是确实担心 克隆一份 在大量分组下 性能损失严重
这个只有再测试了。
2. 子节点不能先于父节点出现
当然 如果你是走正常添加,ID顺序的话 确实没有问题;
所以我添加了一个 errerGroup 来存放找不到父节点,或者父节点后于子节点出现的情况
PS: 家里vs 出问题了。 上面代码记事本纯手写也没有测试过.
补上测试后的代码
FinishingTree = {
newGroup: {},
errorGroup: {},
pidTag: "pid",
indexTag: "id",
tmpGroup: {},
Deep: function(json) {
var arr, g;
for (var i in json) {
g = json[i];
arr = this.FindNodes(g);
if (arr == -1) continue;
this.SetNodes(g, arr);
}
},
FindNodes: function(g, arr) {
if (!arr) arr = [];
if (g[this.pidTag] == 0) { //顶级分类
return arr;
}
else {
var pGroup = this.tmpGroup[g[this.pidTag]];
if (!pGroup) {
this.errorGroup[g[this.pidTag]] = g;
return -1;
}
else {
arr.push(g[this.pidTag]);
return this.FindNodes(pGroup, arr);
}
}
},
SetNodes: function(g, arr) {
if (!arr || arr.length == 0) {
this.newGroup[g[this.indexTag]] = g;
}
else {
var tmpGroup = this.newGroup;
for (var i = arr.length - 1; i >= 0; i--) {
tmpGroup = tmpGroup[arr[i]];
}
tmpGroup[g.id] = g;
}
this.tmpGroup[g.id] = { pid: g[this.pidTag] };
},
GetNewGroup: function(json, strPID, strIndex) {
if (strPID) this.pidTag = strPID;
if (strIndex) this.indexTag = strIndex;
var tmpArr = [];
tmpArr.push(json);
var back = tmpArr.slice(0);
this.Deep(back[0]);
delete back;
this.tmpGroup = {};
return this.newGroup;
}
}
IE6下 4毫秒的样子
就是这样
PS. 最后让我纠结的是。后来我才发现性能问题的源头 tm 不是在这里