p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px ".PingFang SC"; color: #454545 }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC"; color: #454545 }
p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545 }
p.p4 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px "Helvetica Neue"; color: #454545 }
p.p5 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545; min-height: 14.0px }
li.li2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC"; color: #454545 }
li.li3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #454545 }
span.s1 { font: 12.0px "Helvetica Neue" }
span.s2 { color: #e4af0a }
span.s3 { font: 12.0px ".PingFang SC" }
ul.ul1 { list-style-type: hyphen }
浏览器加载渲染过程
- 自上而下加载,加载过程中解析渲染
- 加载中遇到外部css,img会发出异步请求,不会影响文档加载
- 加载中遇到js文件会挂起渲染进程,要等js加载和执行完,才恢复html渲染线程(因为js会修改DOM,document.write,解决方法是把js引用放在</body>前)
- css加载不影响js文件加载,但却影响js文件的执行,即使js只有一行代码也会造成阻塞(
原因:$(“#id”).width(),js执行前,css必须下载解析完成,这事css阻塞后续js的原因。解决办法:当js不需要依赖css时可以放在头部css前面
)
- 不要在外部调用的js文件中调用运行时间较长的函数,如果一定要用,可以使用setTimeout函数。
原因:浏览器有以上五个常驻线程
1、浏览器GUI渲染线程
2、javascript引擎线程
3、浏览器定时器触发线程(setTimeout)
4、浏览器事件触发线程
5、浏览器http异步请求线程(.jpg <link />这类请求)
这里也涉及到 阻塞 的现象,当js引擎线程(第二个)进行时,会挂起其他一切线程,这个时候3、4、5这三类线线程也会产生不同的异步事件(这句话不懂啊),由于 javascript引擎线程为单线程,所以代码都是先压到队列,采用先进先出的方式运行,事件处理函数,timer函数也会压在队列中,不断的从队头取出事件,这就叫:javascript-event-loop。
- 现代浏览器存在 prefetch 优化,浏览器会另外开启线程,提前下载js、css文件,需要注意的是,预加载js并不会改变dom结构,他将这个工作留给主加载。
- 如果js不会改变文档,可以在script标签加 defer 使浏览器先在加载后面的文档
解析
- html文档解析生成解析树即dom树,是由dom元素及属性节点组成,树的根是document对象。
- css解析将css文件解析为样式表对象。该对象包含css规则,该规则包含选择器和声明对象。
- js解析因为文件在加载的同时也进行解析,详看js加载部分。
渲染
渲染最大的一个困难就是为每一个dom节点计算符合他的最终样式。
#test p{ color:#999999}
遍历是自右向左,也就是先查询到p元素,再找到上一级id为test的元素。css解析时,生成的样式对象,从树的低端向上遍历。
- 样式数据是非常大的结构,保存这样是的数据是很耗内存
- 选择器迭代太深,造成太多的无用遍历
- 样式规则涉及非常复杂的级联,定义了规则的层次
- 渲染过程中,webkit使用一个标志位标志所有顶层样式都已经被加载完毕,如果dom元素进行attach时,css元素并没有被加载完毕,则放置占位符,并在文档中标记,当样式表加载完毕,则重新进行计算。
说明:文档的渲染还是要等待顶层css加载完毕。
- 页面的渲染 需要首先生成dom树,再由css生成CSSOM,最后两个一起生成render tree。
event-loop
- 所有同步任务在主线程执行栈执行
- 主线程之外有一个任务队列(事件/消息队列),异步任务有了结果就会在任务队列中添加一个事件,表示异步任务可以进入执行栈了。只要指定过回调函数,事件发生时就会进入任务队列,等待主线程读取。任务队列是先进先出的队列,
- 主线程中所有同步任务执行完之后会,读取任务队列,异步任务进入执行。由于存在"定时器",主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。
问题:主线程空的时候查看队列,如果主线程一直不空队里里的异步操作就一直延后吗?
答:执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。请看下面这个例子。
- 定时器:setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。
node-event-loop
- process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。
- setImmediate方法则是在当前"任务队列"的尾部添加事件,类似setTimeout(f,0)