一、现象说明

前段时间,业务组同事,发现了一个现象;前台页面查询记录分页(500条记录时),(每条记录的属性又非常的多,达50个左右)页面一直卡在那里,等了将近5分钟左右;100条时虽然有些慢,但也可以在1-2分钟响应过来。第一反应,难道是这帮家伙写的sql后台。。。。又一想也不太可能啊,毕竟没有复杂的业务逻辑,并且数据量也不大。不要瞎猜了,chrome network,timing ;发现后端数据响应很快返回了,那就是页面处理的问题了。好吧,有人说是easyui处理的问题,那我们使用的是easyui,datagrid; 这个用法有这个问题么,上网搜索一下。

二、EasyUI问题

http://www.easyui.info/archives/1435.html------------jQuery EasyUI Datagrid性能优化专题
http://www.2cto.com/kf/201312/262455.html--------大数据加载效率慢,优化解决方法

三、JavaScript引擎和渲染引擎---http://cube.qq.com/?p=16

浏览器是如何把HTML/CSS结合起来的内容呈现到窗口上呢, 不同浏览器略微会有些不同。但基本上都是类似的,见下图:


JavaScript引擎和渲染引擎

浏览器把获取到的html代码解析成1个Dom树,html中的每个tag都是Dom树中的1个节点,根节点就是我们常用的document对象( tag)。dom树就是我们用firebug或者IE Developer Toolbar等工具看到的html结构,里面包含了所有的html tag,包括display:none隐藏,还有用JS动态添加的元素等。
浏览器把所有样式(主要包括css和浏览器的样式设置)解析成样式结构体,在解析的过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式,而firefox会去掉_开头的样式。
dom tree和样式结构体结合后构建呈现树(render tree),render tree有点类似于dom tree,但其实区别有很大,render tree能识别样式,render tree中每个node都有自己的style,而且render tree不包含隐藏的节点(比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到render tree中。注意 visibility:hidden隐藏的元素还是会包含到render tree中的,因为visibility:hidden 会影响布局(layout),会占有空间。根据css2的标准,render tree中的每个节点都称为box(Box dimensions),box所有属性:width,height,margin,padding,left,top,border等。
一旦render tree构建完毕后,浏览器就可以根据render tree来绘制页面了。
如果把这个过程比喻成盖楼的话,回流(reflow)就好比是拆掉重来,而重绘(repaint)就相当于在不破坏结构的情况下对外墙重新粉刷。因此,回流必然会重绘,而重绘不一定会回流。

回流的花销也不小,如果每句JS操作都去回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会把flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。

虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前flush队列,这样浏览器的优化可能就起不到作用了。当你请求向浏览器请求一些style信息的时候,就会让浏览器flush队列,比如:
1. offsetTop, offsetLeft, offsetWidth, offsetHeight
2. scrollTop/Left/Width/Height
3. clientTop/Left/Width/Height
4. width,height
5. 请求了getComputedStyle(), 或者 ie的 currentStyle

当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作。
减少回流、重绘其实就是需要减少对render tree的操作,并减少对一些style信息的请求,尽量利用好浏览器的优化策略。

html在浏览器中会被转化为DOM树,DOM树的每一个节点都会转化为RenderObject, 多个RenderObject可能又会对应一个或多个RenderLayer。浏览器渲染的流程如下:

  1. 获取 DOM 并将其分割为多个层(RenderLayer)
  2. 将每个层栅格化,并独立的绘制进位图中
  3. 将这些位图作为纹理上传至 GPU
  4. 复合多个层来生成最终的屏幕图像(终极layer)。
  5. 将这些位图作为纹理上传至 GPU
  6. 复合多个层来生成最终的屏幕图像(终极layer)。
具体详细信息:http://www.infoq.com/cn/articles/javascript-high-performance-animation-and-page-rendering?utm_source=infoq&utm_medium=popular_links_homepage

四、HTML页面加载和解析流程

1.用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
2.浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
3.浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
4.浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
5.浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
6.服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
7.浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;
8.Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码;
9.终于等到了</html>的到来,浏览器泪流满面……
10.等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径;
11.浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

五、JavaScript加载执行顺序

一、在HTML中嵌入Javasript的方法

直接在Javascript代码放在标记对<script>和</script>之间
由<script />标记的src属性制定外部的js文件
放在事件处理程序中,比如:<p οnclick=”alert(‘我是由onclick事件执行的Javascript’)”>点击我</p>
作为URL的主体,这个URL使用特殊的Javascript:协议,比如:<a href=”javascript:alert(‘我是由javascript:协议执行的javascript’)”>点击我</a>
利用javascript本身的document.write()方法写入新的javascript代码
利用Ajax异步获取javascript代码,然后执行
第3种和第4种方法写入的Javascript需要触发才能执行,所以除非特别设置,否则页面加载时不会执行。

二、Javascript在页面的执行顺序

页面上的Javascript代码是HTML文档的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记<script />的出现顺序, <script />标记里面的或者通过src引入的外部JS,都是按照其语句出现的顺序执行,而且执行过程是文档装载的一部分。
每个脚本定义的全局变量和函数,都可以被后面执行的脚本所调用。
变量的调用,必须是前面已经声明,否则获取的变量值是undefined。
1 <script type="text/javascript">//<![CDATA[
2    alert(tmp);  //输出 undefined
3    var tmp = '111';
4    alert(tmp);  //输出 111
5    //]]>
6 </script>
同一段脚本,函数定义可以出现在函数调用的后面,但是如果是分别在两段代码,且函数调用在第一段代码中,则会报函数未定义错误。
1 <script type="text/javascript">//<![CDATA[
2    test();    //浏览器报错
3    //]]>
4 </script>
5 <script type="text/javascript">//<![CDATA[
6    test();    //输出 fun!
7    function test(){alert('fun!');}
8    //]]>
9 </script>
document.write()会把输出写入到脚本文档所在的位置,浏览器解析完documemt.write()所在文档内容后,继续解析document.write()输出的内容,然后在继续解析HTML文档。
01 <script type="text/javascript">//<![CDATA[
02    document.write('<script type="text/javascript" src="test.js"><\/script>');
03    document.write('<script type="text/javascript">');
04    document.write('alert("222");')
05    document.write('alert("变量保存值" + tmpStr);');
06    document.write('<\/script>');
07    //]]>
08 </script>
09 <script type="text/javascript">//<![CDATA[
10    alert("333");
11    //]]>
12 </script>
test.js的内容是:

1 var tmpStr = '111';
2    alert(tmpStr);
在Firefox和Opera中的弹出值的顺序是:111、222、变量保存值111、333
在IE中弹出值的顺序是:222、111、333, 同时浏览器报错:tmpStr未定义
原因可能是IE在document.write时,并未等待加载SRC中的Javascript代码完毕后,才执行下一行,所以导致222先弹出,并且执行到document.write(‘document.write(“变量保存值” + tmpStr)’)调用tmpStr时,tmpStr并未定义,从而报错。

要解决这个问题,可以利用HTML解析时解析完一个HTML标签,再执行下一个的原理,把代码拆分来实现(上面第一段js分拆成两段):

01 <script type="text/javascript">//<![CDATA[
02    document.write('<script type="text/javascript" src="test.js"><\/script>');
03    //]]>
04 </script>
05 <script type="text/javascript">//<![CDATA[
06    document.write('<script type="text/javascript">');
07    document.write('alert("222");')
08    document.write('alert("变量保存值" + tmpStr);');
09    document.write('<\/script>');
10    //]]>
11 </script>
12 <script type="text/javascript">//<![CDATA[
13    alert('333');
14    //]]>
15 </script>
这样IE下和其他浏览器输出值的顺序一致了:111、222、变量保存值111、333。

三、如何改变Javascript在页面的执行顺序

利用window.onload
1 <script type="text/javascript">//<![CDATA[
2 window.onload = func1;
3 function func1(){alert('111');}
4 alert('222');
5 //]]>
6 </script>
输出值顺序是 222、111。

需要注意的是,如果存在多个winodws.onload的话,只有最后一个生效,解决的办法是:

1 window.onload = function(){f();f1();f2();.....}
利用2级DOM事件类型

1 if(document.addEventListener){
2 window.addEventListener('load',f,false);
3 window.addEventListener('load',f1,false);
4 ...
5 }else{
6 window.attachEvent('onload',f);
7 window.attachEvent('onload',f1);
8 ...
9 }
IE中可以利用defer,defer作用是把代码加载下来,并不立即执行,等文档装载完毕之后再执行,有点类似window.onload,但是没有window.onload那样的局限性,可以重复使用,但是只在IE中有效,所以上面的例子可以修改成为
01 <script type="text/javascript">//<![CDATA[
02 document.write('<script type="text/javascript" src="test.js"><\/script>');
03 document.write('<script type="text/javascript" defer="defer">');
04 document.write('alert("222");')
05 document.write('alert("变量保存值" + tmpStr);');
06 document.write('<\/script>');
07 //]]>
08 </script>
09 <script type="text/javascript">//<![CDATA[
10 alert("333");
11 //]]>
12 </script>
这样IE就不报错了,输出值的顺序变成:111、333、222、变量保存值111

当HTML解析器遇到一个脚本,它必须按常规终止对文档的解析并等待脚本执行。为了解决这个问题HTML4标准定义了defer。通过defer来提示浏览器可以继续解析HTML文档,并延迟执行脚本。这种延迟在脚本从外部文件载入时非常有用,让浏览器不必等待外部文件全部载入之后才继续执行,能有效的提高性能。IE是目前唯一支持defer属性的浏览器,但IE并没有正确的实现了defer属性,因为延迟的脚本总是被延迟,直到文档结束,而不是只延迟到下一个非延迟的脚本。这意味着,IE中延迟的脚本的执行顺序相当混乱,并且不能定义任何后面非延迟脚本并须的函数和变量。在IE中所有的defer的脚本执行时间应该都是HTML文档树建立以后,window.onload之前。

利用Ajax。
因为xmlhttpRequest能判断外部文档加载的状态,所以能够改变代码的加载顺序。
4、一些注意事项:

以上所讲的deffer看似用起来没什么问题,但是实际上无法兼容Mozilla。所以通常我们还是要借助window.onload来实现。但是,之后就没有问题了么?假设页面dom里有一张图片,像这样:

1 <img src="picture.jpg" >
而这张图片又非常之大(例如30MB,这个数字虽然有点极端,但真的在有些网站出现过!),那么在dom加载完毕之前,js是无法执行的。问题就在于假设dom提供了用户交互的功能。例如按钮,输入表单等,这个时候他们已经是被呈现了的,因此就很有可能产生无效的用户行为。

我们不能指望用户像我们预期的那样等待页面显示加载完毕后再发生动作,而要把用户考虑成随时随刻会到处乱点的朋友。

这个问题又如何解决呢?既然我们需要页面结构输出后执行js,我们不妨把js入口函数定义在页面最下面好了。

1 <head>
2  <script src="x.js" type="text/javascript"></script>
3 </head>
4 <body>
5   ......
6  <img src="picture.jpg" >
7  <script type="text/javascript">init();</script>
8 </body>
这样就达到我们的目的了,页面结构输出完毕后就执行js,不用考虑图片的加载。

但是在文档末尾嵌入一条js脚本,毕竟容易被忽略,把关键的程序入口放在这种渺小的角落,总觉得不太合适。那有什么预留退路的方法没有呢?

我们可以把结尾的脚本稍微修改一下:

1 <head>
2  <script src="x.js" type="text/javascript"></script>
3 </head>
4 <body>
5   ......
6  <img src="picture.jpg" >
7 <script type="text/javascript">window.onload();</script>
8 </body>
而在js里预先把入口定义给onload事件:

1 window.onload = function() {
2 alert("load over");
3 }
这时候页面结构加载完毕后就会调用onload函数,而即使漏写了dom里的onload入口,js自身里的onload定义也会在页面加载完毕后执行,这样退路就留出来了。

不过这时候有个问题,onload事件会执行两次,可以在js的onload实现里解决这个问题,改成这样:

1 var flag = false;
2 window.onload = function() {
3  if (flag) {return;}
4   flag= true;
5   alert("load over");
6 }
这样似乎已经解决我们所有的问题了,不过仍然有些小遗憾,因为最后一行代码,致使行为与结构没有分离开来,要 unobtrusive 就要 unobtrusive 的彻底,为了达到完美的分离,还有很大的讨论空间。

而对于js文件内部的onload事件,我们还可以参考 Simon Willison 的addLoadEvent函数来优化:

01 function addLoadEvent(func) {
02  var oldonload = window.onload;
03  if (typeof window.onload != 'function') {
04     window.onload = func;
05   } else {
06     window.onload = function() {
07      if (oldonload) {
08         oldonload();
09       }
10       func();
11     }
12   }
13 }
然后,我们就可以在js里肆无忌惮地不停地将各个不同的函数添加到onload事件响应中了:

1 addLoadEvent(funcA);
2 addLoadEvent(funcB);
3 addLoadEvent(funcC);
当然,同一个js里设置多个onload响应函数其实没什么必要,我们完全可以把funcA、funcB、funcC封装在一个函数里add,addLoadEvent函数,更理想的状态是为页面动态调用的多个js文件添加入口。

由网页假死现象查找到的资料相关推荐

  1. 【教程】完美解决windows10磁盘占用100%并出现卡顿、假死现象

    本文原始地址为:http://tieba.baidu.com/p/4359125660 欢迎浏览我的博客:https://fitz1318.top/ lz自从上win10以来经常会出现这种情况:磁盘突 ...

  2. java win10窗口启动假死_window执行jar包会出现假死现象解决

    windows jar包启动的几种方式介绍 java -jar xxx.jar(jar包的路径),最普通简单的方式,通过windows的命令行窗口启动,并在命令行窗口运行,输出.可以在启动的时候增加参 ...

  3. cisco交换机端口“假死”现象

    "假死"现象蔓延 不得不根治? 但是最近几天单位那台连接数字电视前端系统的交换机上也出现了端口"假死"的现象,故障原因很快查清了:是因为该端口下面连接的一台交换 ...

  4. 教你几招解决电脑假死现象

    不少电脑使用者在平常的生活中,都遇到过电脑"假死"的现象,也就是长时间卡机不动,点击什么都没反应.这让我们很无奈,尤其是在工作中,文档写了一半,突然死机,运气好还能恢复,运气不好的 ...

  5. java程序假死_分析一个常见的java多线程通信问题(假死现象)

    一件复杂的事,一个人如果不能做,两个人又做的不好,一群人就可能很好的解决了.对于线程来说也是,通过多个线程就能完成一个更复杂的功能,这就需要多个线程协作,协作就需要交流,但是交流总是会出问题的.在这篇 ...

  6. tomcat 假死现象(转)

    1.1 编写目的 为了方便大家以后发现进程假死的时候能够正常的分析并且第一时间保留现场快照. 1.2编写背景 最近服务器发现tomcat的应用会偶尔出现无法访问的情况.经过一段时间的观察最近又发现有台 ...

  7. ubuntu文件管理器打不开,出现假死现象的解决办法记录

    ubuntu文件管理器打不开,出现假死现象的解决办法记录 无论新建多少窗口,多少次双击一直出现这种文件管理器界面,一直进不去. 经了解, 一般是nautilus出错了,才出现假死现象,可以卸载之后重新 ...

  8. hibernate运行一段时候后出现假死现象

    最近在维护一个外包遗留下的Hibernate+spring+spring mvc的一个项目,发现该项目运行一段时间后,发现APP请求后一直处于等待状态,直到请求超时,于是调试跟踪了一下,发现是操作数据 ...

  9. html让页面卡住,网页假死 网页总是假死,失去响应怎么办

    c++,static_cast 一般是 浏览器假死 浏览器假死可能有以下原因: 打印网页内容,浏览器假死,如何解决?网络打印机,打word可以,但打印网页表格时,点击打印按钮,页面就无反在I浏览器图标 ...

最新文章

  1. 2019-03-20 Python爬取需要登录的有验证码的网站
  2. 清华大学唐杰教授:人工智能的十年总结
  3. NOIP2018初赛提高组复习提纲(By HGOI LJC)
  4. 【学习笔记】13、标准数据类型—元组
  5. 神仙情侣:一起读研读博,然后结婚……
  6. Linux内核调试的方式以及工具集锦
  7. cfree mysql_如何配置CFree才能开发MySql数据库应用 | 学步园
  8. 使用一键重装工具制作U盘启动盘失败的解决方法
  9. 【应急响应】Linux应急响应入侵排查思路
  10. Vue项目中 实现ElementUi框架el-select拼音搜索功能
  11. 翻译学习 | 混合线性模型的思考
  12. 知识图谱的架构及关键技术概述
  13. Java实现DOC文件转DOCX文件
  14. 2020复旦计算机专硕英语,2020双非二战复旦计算机专硕392经验贴
  15. 前端css基础知识点之PC端项目-规范
  16. 《Python3 网络爬虫开发实战》:关系型数据库 MySQL 存储
  17. 研华PCI板卡开发(4)快速入门(4)轴操作
  18. C语言学生管理系统项目
  19. c语言代码学生学籍系统,C语言实现学生学籍管理系统.pdf
  20. 012-基于 git hooks 的前端代码质量控制解决方案

热门文章

  1. 【轻松无忧从Windows XP升级Windows 7 】
  2. Jenkins执行Python代码时提示‘taskkill‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  3. 内网穿透(mac,window,linux通用)1分钟实现外网访问电脑本地服务器
  4. android otaupdate源码路径,Android学习之OTA Update
  5. HTML5基础 (day3)
  6. kali安装Nessus与离线更新
  7. 树莓派——智能家居第一步
  8. Java 1020 月饼
  9. toast插件的简单封装(样式适用pc后台管理系统的场景)
  10. 如何教会老婆写 Python ?