前端知识点之浏览器
1.Node中的事件循环
node.js运行机制:V8引擎解析js脚本;解析后的代码,调用node API,libuv库负责Node API的执行,将不同的任务分给不同的线程,形成一个事件循环,以异步的方式将任务的返回结果返回给V8引擎,v8引擎再将结果返回给用户。其中libuv引擎中的事件循环分为6个阶段,它们会按照顺序反复运行,每当进入某一个阶段的时候,都会从对应的对应的回调队列中取出函数去执行,当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。事件循环的顺序:外部输入数据-》轮询阶段(poll)-》检查(check)-》关闭事件回调(close callback)-》定时器检测阶段(timer)-》I/O事件回调阶段(I/O callbacks)-》闲置阶段(idle,prepare)-》轮询阶段.......
2.浏览器内核
多线程GUI渲染线程:主要负责页面渲染,解析HTML、CSS,构建DOM树,布局和绘制等,当页面需要重绘或由于某种操作引发某种回流时,将执行该线程,该线程与js线程互斥,当执行js引擎线程时,GUI渲染会被挂起,当任务队列空闲时,js引擎才会去执行GUI渲染。
JS引擎线程:主要负责js脚本,执行代码,也负责执行准备好待执行的事件,即定时器计时结束或者异步请求成功并正确返回时,将依次进入任务队列,等待JS引擎线程的执行,当JS执行脚本时间过长,将导致页面渲染的阻塞。
定时器触发线程:负责执行异步定时器一类的函数的线程,主线程执行代码时,遇到定时器,会将定时器交给该线程处理,当计数完毕后,时间触发线程会将计数完毕后的时间加入到任务队列的尾部,等待JS引擎线程执行。
事件触发线程:将准备好的事件交给JS引擎线程执行;
异步HTTP请求线程:负责执行异步请求的函数的线程
3.浏览器事件机制
3.1事件是什么?事件模型?
按下键盘、点击鼠标是事件,还可能是web浏览器中发生的事情,如某个web页面加载完成,或者是用户滚动窗口或改变窗口大小;UI事件:load,unload,error,select,resize,scroll。事件模型:原始事件模型(DOM0级),事件发生后没有传播的概念,没有事件流,事件发生,立即处理,监听函数只是元素的一个属性值,通过指定元素的属性值来绑定监听器,优点是所有浏览器都兼容,缺点是逻辑与显示没有分离,只能绑定一个监听函数,后绑定的会覆盖前面的,无法通过事件的冒泡、委托等机制;IE事件模型,将event对象在处理函数中设为window的属性,一旦函数执行结束,编被置为null了,事件模型分为两步,先执行元素的监听函数,然后事件沿着父节点一直冒泡到document,缺点是只能IE自己用;DOM2事件模型,W3C制定的。
一次事件的发生包含三个过程,即捕获(事件被从document一直向下传播到目标元素,在过程中依次检查经过的节点是否注册了该事件的监听函数,若有则执行)、处理(事件到达目标元素,执行目标元素的事件处理函数)、冒泡(从目标元素上升一直到达document,同样依次检查经过的节点是否注册了该事件的监听函数,有则执行)等。注:所有事件都会经历事件捕获阶段,只有部分事件会经历事件冒泡如submit。
3.2事件捕获和事件冒泡
冒泡的使用场景:事件冒泡允许多个操作被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元素上),它还可以让你在对象层的不同级别捕获事件;让不同的对象同时捕获同一事件,并调用自己的专属处理程序做自己的事情,就像老板一下命令,员工回自己的岗位做事情。3.3对事件委托/事件代理的理解,及使用场景
把事件监听函数绑定到父元素上,让它的父辈来完成事件的监听。利用冒泡的原理,把事件加到父级上,监听自己的事件,来触发执行效果。作用:支持为同一个DOM元素注册多个同类型事件,可将事件分成事件捕获和事件冒泡机制,提高性能,每个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少,动态监听,使用事件委托可以自动绑定动态添加的元素,即新增的节点不需主动添加也可以一样具有和其他元素的一样的事件。通俗的解释就是一个公司的员工收到快递后让前台代收,因为这样就不用一个一个打电话,节约时间、提升性能。3.4对事件循环的理解
所有任务分为两种,同步任务和异步任务,同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕才能执行后一个任务;异步任务指的是,不进入主线程而进入“任务队列“的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。异步执行的运行机制如下:所有同步任务都在主线程上执行,形成一个执行栈,主线程之外,还存在一个“任务队列”,只要异步任务有了运行结果,就在“任务队列”种放置一个事件,一旦“执行栈”中步任务执行完毕,系统就会读取"任务队列“,看里面有什么事件,哪些对应的异步任务,于是结束等待状态,进入执行栈,开始执行,主线程重复以上步骤。3.5宏任务和微任务分别有哪些
宏任务由宿主发起,而微任务由js自身发起。宏任务:script,setTimeout/setInterval,UI rendering/UI事件,postMessage,MessageChannel,setImmediate,I/O.微任务:Promise.then,MutaionObserver,process.nextTick()执行方式就是,执行一个宏任务,过程中遇到微任务时,将其放到将其放到微任务的事件队列里,当前宏任务执行完成后,会查看微任务的事件队列,依次执行里面的微任务,如果还有宏任务的话,再重新开始宏任务...
3.6微任务为什么先于宏任务执行
减少更新时的渲染次数,因为根据HTML标准,会在宏任务执行结束之后,在下一个宏任务开始执行之前,UI都会重新渲染,如果在microtask就完成数据更新,当macrotask结束就可以得到最新的UI了,如果新建一个macrotask来做数据更新的话,那么渲染就会执行两次。4浏览器同源策略
4.1什么是同源策略
浏览器最核心、最基本的安全功能。同源指域名、协议、端口相同;同源策略又分为以下两种,DOM(禁止对不同源页面DOM进行操作,主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的)和XMLHttpRequest(禁止使用XHR对象向不同源的服务器地址发起HTTP请求)。用于限制一个origin的文档或者它加载的文本如何能与另一个源的资源进行交互,出于防范跨站脚本的攻击,禁止客户端脚本对不同域的服务进行跨站调用,能帮助阻隔恶意文档,减少可能被攻击的媒介。cookie,localStorage和IndexDB无法读取,DOM无法获得Ajax请求不能发送。
为什么要有跨域限制:存在同源策略,就会有跨域问题,为了用户的上网安全。若没有DOM同源策略,不同域之间的iframe之间可以相互访问,若没有XHR同源策略,黑客可以进行CSRF。
4.2如何解决跨域问题
JSONP跨域:script标签不受浏览器同源的影响,允许跨域引用资源,过程就是:声明一个回调函数,其函数名做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据;创建script标签,把那个跨域的API数据接口地址赋值给script的src,还要在这个地址中向服务器传递该函数名;服务器接收到请求,进行处理,把传递进来的参数名和它需要给的数据连接成一个字符串;最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用回调函数对返回的数据进行操作。CORS:跨域资源共享,定义了在访问跨域资源时,浏览器与服务器应该如何沟通,浏览器自动完成,不需用户参与,浏览器发现跨域,则会添加一些附加的头信息。分为简单请求和非简单请求,简单请求同时满足两大条件,请求条件是HEAD、GET、POST三者之一,HTTP的头信息不超出Accept、Accept-Language、Content-Language,Last-Event-ID,Content-Type这几种。复杂请求会在正式通信之前,增加一次HTTP请求,称为“预检”请求,该请求是option方法的,通过该请求来知道服务端是否允许跨域请求。
postMessage:是H5 xhr中的API,且是为数不多的可以跨域操作的window属性之一,可以解决:页面和其打开的新窗口的数据传递;多窗口之间数据传递;页面与嵌套的iframe数据传递;上面三个场景的跨域数据传递。
websocket:实现了浏览器和服务器间的全双工通信,建立连接后,server和client都能主动向对方发送或接收数据。
node中间件代理(两次跨域):同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略,代理服务器需要做到如下步骤:接收客户端请求,将请求转发给服务器,拿到服务器响应数据,将响应转发给客户端。
nginx反向代理:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。是最简单的跨域方式,只需修改配置。
图像Ping跨域:由于img标签不受浏览器同源策略的影响,允许跨域引用资源,因此可以通过img的src属性进行跨域,用于实现跟踪用户点击页面或动态广告曝光次数有较大优势,但是只支持GET请求,只能浏览器与服务器的单项通信,因为浏览器不能访问服务器的响应文本。
服务器代理:浏览器存在跨域问题,但服务器不存在跨域问题,所以可以由服务器请求所有域的资源再返回给客户端,服务器代理是万能的。
document.domain跨域:适用于主域名相同而子域名不同的情况,可以使用document.domain来跨域,这种方式适用于iframe跨域的情况 。只要把两个页面的document.domain都设成相同的域名就可以了,但是必须是自身或更高一级的父域且主域必须相同。
window.name跨域:window对象有个name属性,该属性由个特征:即再一个窗口的生命周期内,窗口载入的所有页面(不管是相同域还是不同域的页面)都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的饿载入而进行重置。
5.浏览器本地存储
5.1浏览器本地存储方式及使用场景
localStorage:生命周期永久,除非清除浏览器中的localStorage信息,否则永远存在,存放数据大小一般为5MB,仅在浏览器中保存,不参与服务器通信;sessionSorage:仅在当前会话下有效,关闭页面会浏览器后被清除,存放数据大小一般为5MB,仅在浏览器中保存,不参与服务器通信;Cookie:在设置的过期时间内一直有效,即使窗口或浏览器关闭,存放数据大小为4K,有存储个数限制(与浏览器有关),一般不超过20个,与服务端通信,每次都会携带在HTTP头中,Cookie存储数据过多会带来性能问题。应用场景:localStorage的大容量和持久特性,可以存储一些内容稳定的资源,比如官网logo ,图片资源等。
5.2cookie,localstorage和sessionstorage的区别
localStorage和sessionStorage的区别:不同浏览器无法共享这两者的信息,相同浏览器的不同页面可以共享localStorage信息,不能共享sessionStorage的信息;sessionStorage,对表单数据进行维护,保证页面刷新也不会让之前的信息丢失,也可以用它存储本次浏览记录,若关闭后不需要这些记录,可以用该存储方法,比如微博;Cookie不适合存储,有很大缺陷,cookie包括会话cookie和持久cookie,用户退出浏览器,会话cookie就会被删除了,持久cookie就会存储在硬盘里,保留时间更长,设置了属性secure,cookie只有在https协议加密情况下才会发送给服务端,但是这并不是最安全的,可以通过document.cookie设置和获取cookie的值。cookie一般是被浏览器以txt的形式存储在电脑硬盘中,供该浏览器进行读写操作;当浏览器发起请求时,浏览器会自动检查是否有相应的cookie,如果有则将cookie添加到request Headers的cookie字段中,当服务端需要种cookie时,在http请求的response Headers中添加set-cookie字段,浏览器接收到之后会自动解析识别,将cookie种下。
5.3前端存储的方式
包括离线缓存(application cache),web SQL,IndexedDB以及本地存储的cookie,LocalStorage,sessionStorage.6.浏览器渲染原理
6.1浏览器的渲染过程
关键渲染路径:浏览器从最初接收请求来的HTML,CSS,JS等资源,然后解析、构建树、渲染布局、绘制,最后呈现给用户能看到的界面的过程。用户看到页面分两个阶段,DOMContentLoaded(事件触发时,仅当DOM加载完成,不包括样式表和图片等)和Load(事件触发时,页面上所有的DOM、样式表、脚本和图片都已加载完成)。渲染过程包括五步:将获取的HTML文档加息成DOM树;处理CSS标记,构成层叠样式表模型CSSOM;将DOM和CSSOM合并为渲染树,代表一系列被渲染的对象;渲染树的每个元素包含的内容都是计算过的,被称为布局Layout;将渲染树各个节点绘制到屏幕上,称为绘制painting。构建DOM树:构建过程中可能会被CSS和JS的加载而执行阻塞,display:none和注释及script标签也在树中;构建CSSOM规则树:每个CSS文件都被分析成一个StyleSheet对象,每个对象都包含CSS规则,CSS规则对象包含对应于CSS语法的选择器和声明对象以及其他对象;DOM和CSSOM可以同时解析,但CSS与script的执行互斥;且两者的解析都要经过Bytes->characters->tokens->nodes->object model.构建渲染树:浏览器从DOM树的根节点开始遍历每个可见节点,然后对每个可见节点找到适配的CSS样式规则并应用,RenderTree和DOMTree不完全对应,display:none的元素不在RenderTree中,visibility:hidden的元素在RenderTree中;渲染树布局:从渲染树的根节点开始遍历,渲染树的每个节点都是一个Render对象,包含宽高、位置和背景色等信息,所以浏览器根据这些样式西南西来确定每个节点对象在页面上的大小和位置,布局阶段的输出就是常说的盒子模型,注意:float元素、absolute元素、fixed元素都会发生位置偏移,常说的脱离文档流指的指的是脱离RenderTree;渲染树绘制:浏览器遍历渲染树,调用渲染器的paint()方法在屏幕上显示内容,由浏览器UI后端组件完成。
渲染阻塞:JS可以操作DOM改变DOM结构,可以操作CSSOM改变它的结构,这就导致浏览器在遇到script标签时DOM构建暂停直到脚本完成执行,若脚本是外部的,会等脚本下载完毕,再继续解析现在可以增加defer属性或async,会将脚本中改变这两个树的地方分别解析出来,然后追加到两个树上。CSS阻塞渲染意味着会一直白屏。
回流和重绘:当布局发生变化,倒回去重新渲染的过程叫回流。会从根节点开始递归往下,依次计算尺寸和位置,确认发生变化的地方。重绘:当改变某个元素的背景色、文字颜色等不影响布局的属性时,会触发。display:none会触发回流,visibility:hidden会触发重绘。
6.2浏览器渲染优化
合法书写HTML和CSS,不要忘了文档编码类型;样式文件在head标签中,脚本文件在body结束前,防止阻塞;将CSS嵌套层减少到最小;DOM的多个读操作(或写操作)应该放在一起;若某个样式是通过重排得到的,最后缓存结果,避免下次用的时候浏览器又要重排;通过改变class或CSStext属性一次性改变样式,而不是一条一条改变;用transform作形变和位移;position为absolute或fixed的元素,重排开销较小。7.浏览器组成
常见的浏览器内核IE(Trident)、火狐(Gcko)、谷歌和苹果旧版本(webkit)、谷歌和欧朋(Blink)
对浏览器内核的理解
分为渲染引擎和JS引擎,渲染引擎:负责取得网页的内容(HTML,XML和图像等)、整理讯息以及网页的计算方式,然后会输出至显示器会打印机;JS引擎:解析和执行JavaScript来实现网页的动态效果
8.浏览器缓存
对浏览器缓存机制的理解缓存位置:service worker,是运行在浏览器背后的独立线程,可以用来缓存;memory cache,内存缓存,主要包含当前页面中已经抓取到的资源,读取速度快,但持续性短,会随着进程的释放而释放,有一块重要的缓存资源是preloader相关指令,可以一边解析js/css文件,一边网络请求下一个资源;disk cache,是存储在硬盘中的缓存,读取速度慢,但是什么都能存,优点是容量和存储时效性;push cache ,推送缓存,以上三种缓存没有命中时,才能被使用,只是在会话中存在,会话结束就会被释放。缓存过程:浏览器发起http请求,先在浏览器缓存中查找该请求的结果以及缓存标识,若没有则会向服务器发起请求,服务器返回请求结果,浏览器将该请求结果和缓存标识存入浏览器缓存中。
协商缓存和强缓存的区别
强缓存:不会向服务器发起请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,size显示from memory cache或from disk cache,可以通过设置两种HTTP header 实现,Expires(缓存过期时间,用来指定资源到期的时间,是服务端的具体时间点,即Expires=max-age+请求时间,需要和Last-modified结合使用,在响应请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存读取数据,无需再次请求)和Cache-control(控制网页缓存,多种指令,如max-age,设置缓存存储的最大周期)。
区别在于:前者是http1.0的产物,后者是1.1的,同时存在的话cache-control的优先级高,某些不支持1.1的情况下,Expires发挥作用。判断依据是是否超粗某个时间段,不关心服务器端文件是否已经更新,若要知道是否更新,则用到协商缓存。
协商缓存:强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存。协商缓存生效,返回304和Not Modified,表示资源无更新,在缓存中获取结果;协商缓存成功,返回200和请求结果,后将请求结果存入缓存中。可通过设置Last-Modified(资源文件在服务器最后被修改的时间,根据If-Modified-Since的字段值与最后修改时间对比,若最后被修改时间大于If-Modified-Since的值,则重新返回资源,状态码为200)和ETag(当前资源文件的一个唯一标识符,与If-None-Match作对比,一致则返回304,代表资源无更新,继续使用缓存,否则重新返回)实现,后者优先级高于前者。
9.浏览器安全
9.1什么是XSS,如何防御XSS
XSS指的是跨站脚本攻击,是一种代码注入攻击。攻击者在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如cookie等。本质是因为网站没有对恶意代码进行过滤,与正常代码混合在一起,浏览器无法分辨哪些脚本可信,从而导致恶意代码的执行。分为存储型、反射型和DOM型,存储型指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行;反射型指的是攻击者诱导用户访问一个带有恶意代码的URL,服务端接收数据后处理,把带有恶意代码的数据发送到浏览器端,浏览器解析这段带有XSS代码的数据后当作脚本执行,最终完成攻击;DOM型指的通过修改页面的DOM节点形成的XSS。反射型跟存储型的区别是存储型的恶意代码存在数据库里,反射型的存在URL里,DOM跟前两者的区别是DOM攻击中,取出和执行恶意代码由浏览器端完成,属于前端js自身的安全漏洞,其他两者是服务端的安全漏洞。防御:可以从浏览器的执行来预防,一种是使用纯前端的方式,不用服务端渲染,另一种是对插入到HTML中的代码做好转义;也可以用CSP,即建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击;对敏感信息进行保护,如cookie使用http-only,使得脚本无法获取,也可以使用验证码。
9.2什么是CSRF,如何防御
CSRF指的是跨站请求伪造攻击,攻击者诱导用户进入第三方网站,然后该网站向被攻击网站发送跨站请求,若用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录绕过后台的验证,冒充用户向服务器执行一些操作,本质是利用cookie会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。分为GET,POST和链接类型的攻击。 防御:进行同源检测,服务器根据http请求头中origin或referer信息来判断请求是否为允许访问的站点,两者都不在时,阻止请求,缺点是referer可以伪造;token验证,解决cookie单一验证的缺点,缺点是操作繁琐;严格模式。
--------------------------------------------------------
欢迎批评指正~
另附