恶补javascript基础_3
参考:
- 博客园:网页在浏览器上的渲染过程
- csdn:浏览器是如何渲染页面的?
- w3cschool:CSS visibility 属性
- 在线工具:HTTP状态码详解
- segmentfault:浏览器渲染页面过程与页面优化
- 简书:网页的首屏,你知道多少?
- 博客园:【前端性能】高性能滚动 scroll 及页面渲染优化
- segmentfault:前端性能优化(DOM操作篇)
- 博客园:前端优化:DNS预解析提升页面速度
- csdn:前端优化之DNS预解析
- 简书:浏览器是如何渲染页面的?如何优化首屏渲染性能?
- csdn:HTTP请求头部+响应码
- csdn:访问一个网页的全过程
- 博客园:URI与URL区别
- segmentfault:不要再问我跨域的问题了
- HarttleLand:Cookie/Session的机制与安全
- 官网:浏览器的同源策略
- 博客园:javascript页面刷新的几种方法
文章目录
1. 浏览器渲染页面
这一部分有七分之六的内容来自网页在浏览器上的渲染过程
主要步骤:解析html->构建DOM Tree->构建渲染树->布局渲染树->绘制渲染树
- 浏览器将html解析成DOM Tree,DOM Tree的构建是通过深度遍历结点实现的
- 浏览器将CSS解析成CSS规则树
- 浏览器会根据DOM Tree和CSSOM来构造渲染树
- 浏览器会根据前三步获得的DOM Tree的节点关系以及样式来计算结点在屏幕中的位置。这个操作成为布局(Layout)。
- 浏览器遍历渲染树,绘制结点。
上面这5个步骤虽然是逐步完成的,但是并不会等全部的html解析完了才开始构建DOM Tree以及后面的步骤。浏览器会解析完 一部分 html之后就去构建 一部分 DOM Tree、渲染树以及布局、绘制 一部分 渲染树。这是浏览器出于效率的考虑。
从上图可以清楚地看到,渲染树并完全不是DOM Tree和CSSOM的合体。
-
DOM Tree的body几点只有两棵子树
<p>
的子树和<div>
的子树,但CSSOM的body结点很明显有三棵子树,多出来了<span>
的子树。所以这个多出来的<span>
不会出现在渲染树中,它是无效的,因为DOM Tree里面没有。 -
DOM Tree的
<p>
结点有个<span>
结点,但是这个<span>
结点在CSSOM里面标明的是display: none;
的样式,也就是不显示,所以也不会被放入到渲染树里面。 -
因为DOM Tree里的
<img>
是<div>
的子结点,而整棵DOM Tree又只有一个<img>
,所以就算CSSOM里面只写了<img>
,渲染树中也会把它变成<div>
的子结点。
html页面加载和解析的流程:
- 用户输入一个html网址并且是第一次访问,浏览器向服务器发出请求,服务器返回html文件。
- 浏览器载入html,如果发现
<head>
里面的<link>
引用外部css文件的话,浏览器会发出CSS文件请求,此时服务器返回CSS文件。 - 浏览器载入
<body>
中的部分代码,然后会构建一部分DOM Tree,因为上一步拿到了CSS文件,所以也会渲染页面。 - 当浏览器发现
<img>
引用了一张图片时,会向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码。 - 当服务器返回图片文件后,页面由于插入图片占了一部分面积,所以只能回到回过头来重新渲染这部分代码,不是从头开始渲染整个页面的代码,只是
<img>
这里开始以及后面渲染了的那部分代码。 - 当浏览器发现
<script>
时,就会马上运行它。 - 如果javascript脚本中执行了一条让浏览器隐藏掉DOM Tree中的某个
<div>
,那么因为页面排版受到影响,所以浏览器又要重新渲染这部分代码。 - 当浏览器发现
</html>
时,意味着渲染结束。 - 但是如果javascript执行了一段彻底替换整个页面的css样式的代码时,那么浏览器会向服务器请求新的CSS文件,当服务器返回这个新的CSS文件时,浏览器会重新渲染页面。
2. 回流和重绘
(1)回流reflow
- 由于DOM结构中的各个元素都有自己的盒子模型,因此会浏览器根据个样式计算元素的尺寸和位置,构建渲染树。这个过程称之为回流。
- 每个页面在第一次加载的时候,会发生一次回流。
- 完成回流后,浏览器会重新绘制受影响的部分到屏幕中。
- 回流必定导致重绘。
会触发reflow的操作:
- 增加、删除、修改DOM结点
- 移动DOM位置
- 绘制动画
- 修改CSS样式
- Resize窗口或者滚动
- 修改网页字体
display: none;
会触发reflow,因为一旦元素被设置为display: none;
,它将不会被加入到渲染树中。而visibility: hidden;
只会触发repaint,因为这个元素仍然占据着页面的空间,只不过它不被看见而已。
(2)重绘repaint
当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器便把这些元素都按照各自的特性进行绘制,于是页面的内容出现了,这个过程就是repaint。
3. 页面渲染优化
(1)做法
- HTML结构层次尽量少,最好不深于六层
(因为DOM Tree的构建是深度遍历,所以越深效率越低。)
- 脚本尽量后放,放在
</body>
前。
(前面也说了,浏览器渲染的时候,如果遇到<script>
就会不继续渲染而去执行javascript,所以尽量等页面渲染完才执行脚本。)
- 少量首屏样式内联放在标签内
(网站的首屏是指打开网站后,在电脑显示屏中所出现的第一屏内容;需要鼠标向下滚动就在电脑显示屏展示出来就是而屏、三屏了。)
(内联放在标签内就是指直接在html标签上加入style=xxx
,因为只有少量,所以就不用从CSS文件中获取样式了。)
-
样式结构层次尽量简单
-
在脚本中尽量减少DOM操作执行,尽量缓存访问DOM的样式信息,避免过度触发回流。
(触发回流的前两个操作就是增加、删除、修改DOM,移动DOM位置。一旦回流就会重绘,重绘时间成本很高。)
-
减少通过javascript代码修改元素样式,尽量使用class名方式操作样式或动画。
-
动画尽量使用在绝对定位或固定定位的元素上。
(因为relative是保留了原有的位置,把元素定位到原有位置的一个相对位置,把动画绑在relative定位的元素上极易影响DOM的位置,触发回流。)
- 隐藏在屏幕外,或在页面滚动时,尽量停止动画。
(因为已经元素不在屏幕里了,怎么动画都看不到,除非是动画让元素显出来。页面滚动的时候,浏览器其实一直在重新渲染,而且次数很高,如果要提高scroll事件的效率,那就涉及到防抖和节流的问题,详情见【前端性能】高性能滚动 scroll 及页面渲染优化。)
- 尽量缓存DOM查找,查找器尽量简洁。
(一个很明显的例子就是document.getElementById(),每一次运行这个函数都特别耗时间,所以如果一开始就把可能要查找的结点存起来,那么性能会有所优化,例如var a = document.getElementById(‘b’)这样先写,后面就直接用a。)
- 涉及多域名的网站,可以开启域名预解析。
(DNS Prefetch,即DNS预获取,是前端优化的一部分。DNS Prefetching是具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能减少用户的等待时间,提升用户体验。详情见前端优化:DNS预解析提升页面速度)
(2)javascript阻塞渲染
前面在说渲染的时候有提到,如果浏览器在渲染的过程中遇到了<script>
,如果javascipt没有显式声明为异步,那就会停止渲染,优先执行javascript代码,也就是javascript阻塞渲染。所以为了实现最佳性能,最好将javascript放到后面执行,并且去除关键渲染路径中任何没必要的javascript。
<script>
标签的defer属性和async属性用于调整脚本的下载和执行顺序,使其不阻塞页面加载。
① defer属性
开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。如果脚本之间有依赖关系,要是用defer属性!
- 只适用于外联脚本!如果
<script>
没有指定src
属性,只是内联脚本,不要使用defer。 - 如果有多个声明defer的脚本,那么浏览器会按顺序下载和执行。
- defer脚本会在DOMContentLoaded和load事件之前执行。
<!-- 代码来源:https://blog.csdn.net/qq_32657025/article/details/79569213 -->
<!-- 文章:《浏览器是如何渲染页面的?》 -->
<!-- 作者:JiajiaAz -->
<script type="text/javascript" src="defer1.js" defer></script>
<script type="text/javascript" src="defer2.js" defer></script>
② async属性
异步下载脚本文件,下载完毕立即解释执行代码。如果脚本之间没有依赖关系,可以使用async属性。
- 只适用于外联脚本!
- 如果有多个声明async的脚本,那么它们的下载和执行都是异步的,所以不能确保它们的先后顺序。
- async会在load事件之前执行,但不能确保与DOMContentLoaded的执行先后顺序。
4. 网页基本响应过程
- 用户输入URL地址。
- 浏览器解析URL获得主机名,将主机名转换成服务器IP地址。
- 浏览器将端口号从URL中解析出来。
- 浏览器给web服务器发送一个http请求。(tcp三次握手)
- 网站服务的永久重定向响应。(如果没有301永久重定向,当一个页面有两个地址时,就像http://www.igoro.com/ 和http://igoro.com/,搜索引擎会认为它们是两个网站,结果造成每一个的搜索链接都减少从而降低排名。而实施301后,就会把访问带www的和不带www的地址归到同一个网站排名下,新网址完全继承旧网址,旧网址的排名等完全清零。)
- 浏览器跟踪重定向地址。(浏览器向服务器发送请求报文(应用层))
- 服务器发回一个html响应。(服务器向浏览器发送响应报文(应用层))
- 关闭连接(tcp四次挥手),浏览器解析文档。(解析渲染)
- 如果文档中有资源(css/js/图片…),重复前三步。
有关DNS查询IP地址的详细过程:访问一个网页的全过程
递归与迭代相结合的域名解析过程:假设需要查找y.abc.com的IP地址
- 客户机向其本地域名服务器发出DNS请求报文。
- 本地域名服务器收到请求后,查询本地缓存,假设没有改记录,则以DNS客户的身份向根域名服务器发出解析请求。
- 根域名服务器收到请求后,判断该域名属于
.com
域,将对应的顶级域名服务器dns.com
的IP地址返回给本地域名服务器。 - 本地域名服务器向顶级域名服务器
dns.com
发出解析请求报文。 - 顶级域名服务器
dns.com
收到请求后,判断该域名属于abc.com
域,将对应的授权域名服务器dns.abc.com
的IP地址返回给本地域名服务器。 - 本地域名服务器向授权域名服务器
dns.abc.com
发起解析请求报文。 - 授权域名服务器
dns.abc.com
收到请求后,将查询结果返回给本地域名服务器。 - 本地域名服务器将查询结果保存到本地缓存,同时返回给客户机。
5. URI和URL的区别
URL是统一资源定位符
URI是统一资源标识符(代表一种标准)
- URI属于URL的更高层次抽象,一种字符串文本标准。
- URI属于父类,而URL属于URI的子类。URL是URI的一个子集。
- URI表示请求服务器的路径(可以是ftp://,可以是telnet://,或者别的),定义这么一个资源。而URL同时说明要如何访问这个资源(http://或者https?/)
图片来源:URI与URL区别
6. 状态码
1xx:信息,请求收到,继续处理
2xx:成功,行为被成功地接受、理解和采纳
3xx:重定向,为了完成请求,必须进一步执行的动作
4xx:客户端错误,请求包含语法错误或者请求无法实现
5xx:服务器错误,服务器不能实现一种明显无效的请求
7. 跨域问题
直接上链接:
不要再问我跨域的问题了
浏览器的同源策略
Cookie/Session的机制与安全