中高级前端面试宝典
作为一名前端开发工程师,要掌握的知识点是多而杂的,在面试刷题阶段,经常没头没脑的,我将面试题系统化,分了好几个系列,祝愿大家(包括我)在这个疫情刚过去的互联网寒冬找到事儿少钱多🤪的工作。
第一章 前端面试宝典之浏览器篇
中高级前端面试宝典之浏览器篇
前言
没有前言,废话不说,马上进入正题!
一、浏览器渲染
浏览器渲染进程
进程和线程
我们先了解一下进程和线程:
进程是 CPU 资源分配的最小单位,线程是 CPU 调度的最小单位。
知乎上有位大佬的比喻我觉得十分的形象有助于理解。
渲染进程
浏览器是多进程的,浏览器的进程有:
- 浏览器主进程
- 第三方插件进程
- GPU进程
- 浏览器渲染进程
浏览器渲染进程内部又是多线程的:
- GUI渲染线程:负责渲染浏览器页面,解析和构建DOM、CSSOM和渲染树,绘制页面。重绘和回流也会执行。
- JS引擎线程:JS内核,解析和运行javascrpit脚本,一个标签页只有一个JS线程。JS线程跟GUI线程是互斥的。
- 事件触发线程:监听和触发事件,处理事件循环(event loop)。比如我们的click事件,JS引擎解析完click事件中的代码,会将该任务放入事件触发线程,等到事件被触发时,事件触发线程会将对应的任务放入待处理队列的队尾,等待JS引擎空闲时处理。
- 定时器触发线程:setTimeOut和setTimeInterval所在的线程。用来计时并触发事件,时间到了以后将事件放入待处理队列的队尾等待执行。所以定时器中的代码并不是准时准点执行的。
- 异步http请求线程:XMLHttpRequest链接后,浏览器新开一个线程来处理。如果状态变更,将回调函数放入待处理队列
运行机制是:宏任务 -> 微任务 -> 调度 GUI 渲染线程 -> 宏任务……
浏览器渲染机制
- 解析html,构建DOM树
- 解析CSS,生成CSSOM树
- 合并dom树和css规则树,生成render渲染树
- 根据render渲染树进行布局
- 为渲染树分层,生成分层树
- 为每个分层树生成绘制列表,并提交到合成线程
- 合成线程将图层分成不同的图块,并通过栅格化将图块转化为位图
- 合成线程给浏览器进程发指令
- 最后:浏览器生成页面并显示
对于浏览器渲染机制,我找到一篇【图解浏览器渲染原理及流程】,想要详细了解和深入理解的大家应该看完就可以了
说完渲染流程不可避免会提到重绘和回流:
- 重绘和回流都是以图层为单位
- 分图层的条件:有3D的CSS属性、video标签、canvas标签、css3动画
- 回流(重排)一定会触发重绘,布局改变、节点增加减少、窗口变大变小等都会回流
- 重绘,如果图层某个元素要重绘,则整个图层都要重绘
阻塞渲染的情况
阻塞解析:阻塞点后面的标签不会被解析,阻塞解析不一定会阻塞其他外部资源的加载
阻塞渲染:阻塞点后面的标签会继续被解析,img,link等会继续发送请求获取外部资源,但不会触发页面渲染(白屏 ),也不会执行 JavaScript 代码
阻塞渲染的情况:
- JS
① 阻塞DOM解析–因为JS有可能对DOM做操作
② 阻塞页面渲染–同上
③ 阻塞后续js的执行
④ js执行和CSS渲染是互斥的 - CSS
① style标签中:由html解析器进行解析,不阻塞浏览器渲染,不阻塞DOM解析
② link引入的,由CSS解析器进行解析,阻塞浏览器渲染,阻塞后面JS执行,不阻塞DOM解析
以上阻塞都不会阻塞浏览器加载外部资源。
浏览器渲染优化
-
JS
a. 尽量放在body后面
b. 使用defer和asyncdefer 在下载完成以后立即异步加载,加载后等待DOM树构建好再执行 async 在下载完成后立即异步加载,加载后立即执行JS代码。
-
CSS
a. style: GUI直接渲染。如果css少,尽量内嵌
b. link 浏览器派发新的线程(HTTP线程)去加载资源文件,GUI渲染进程会继续向下渲染代码
c. @import GUI渲染进程会暂时停止,去服务器加载文件,资源返回之前不会继续渲染 -
DOM树和CSSOM树
a. 减少HTML文件的代码层级
b. 使用语义化标签
c. 减少CSS代码的成绩 -
减少回流和重绘
a. 尽量不要使用table布局
b. 位置变换尽量使用transform
c. 不频繁操作元素样式,可以修改类名
d. 使用absolute和fixed,使元素脱离文档流,新建图层 -
浏览器自身的优化
将所有的回流和重绘操作放在一个队列,固定间隔或者固定内存进行一次批处理。
二、浏览器缓存机制
浏览器缓存机制其实就是HTTP缓存,由HTTP报文的缓存标识决定
缓存位置
缓存位置有四种,优先级如下:
- Service Worker
- 一个服务器与浏览器之间的中间人角色,如果网站注册了service worker,就可以拦截当前网站所有的请求,然后进行判断。
- 需要使用HTTPS协议保障安全,且可以自由控制缓存文件、匹配方式以及如何读取,缓存是持续的
- 是基于web worker的。
- 可以访问cache和indexDB
- Memory Cache
- 内存中的缓存,主要包含当前页面中的资源如:样式、脚本、图片
- 缓存持续性很短,会随着进程的释放而是放
- Disk Cache
存在硬盘中,读取速度较内存更慢,容量和时效更大 - Push Cache
推送缓存,是HTTP2支持
只存在于session,缓存时间很短,chrome中仅仅5分钟左右
并非严格执行HTTP头中的缓存指令
以上四种缓存都没有命中的时候,才会向服务器发送请求。
服务器接收到请求以后,会根据响应报文中的HTTP请求头的缓存标识来决定是否缓存。
强制缓存
向浏览器缓存中查找请求结果,然后根据该结果的缓存规则来决定是否使用该缓存结果的过程。
强缓存的设置通过两种HTTP Header:Expires 和 Cache-Control, HTTP1.1以后不再使用Expires
强制缓存的情况主要由三种:
- 不存在缓存结果和缓存标识,强制缓存失效,就直接向服务器发送请求
- 存在缓存结果和缓存标识,但是结果失效,则协商缓存
- 存在缓存结果和缓存标识,且结果没有失效,强制缓存生效,从缓存中读取数据,http Code为200,size显示form disk cache或者form memory cache
Cache-Control
主要用于控制网页缓存,有很多值:
public
标识响应可以被客户端、代理服务器缓存
private
只有客户端缓存(默认取值)
max-age
缓存秒数
no-store
任何内容都不缓存
no-cache
被缓存但下次请求时,需要验证是否过期(协商缓存)
协商缓存
协商缓存生效时,服务器返回304和not Modified,意思是该资源无更新,直接从浏览器缓存中读取
协商缓存设置header头:Last-Modified 和 ETag
Last-Modified是服务器返回资源的同时,添加在response header中的,值是资源在服务器上最后的修改时间
If-Modified-Since 是浏览器第二次请求的时候加在header头中的,值是Last-Modified的值
ETag是服务器返回的当前资源的唯一标识,只要资源变化,就重新生成ETag
浏览器发送请求的时候就会将ETag的值放在If-None-Match中
总结
强缓存不会向服务器发送请求,而是直接在浏览器缓存中取值
强缓存不生效时才会使用协商缓存,就是向服务器发送请求来决定要不要使用浏览器中的缓存数据
强制刷新Ctrl + F5 发生什么
普通的F5刷新,页面只是普通的刷新一下,获取数据啥的,该强制缓存就强制缓存、该协商就协商
强制刷新Ctrl + F5,浏览器会清空缓存,重新下载资源,并且请求头中也不会有if-None-Macth和if-modified-Since,强迫服务器不准返回304,而是返回一份真正的数据。
浏览器产生乱码的原因
- 网页源代码是gbk的编码,而内容中的中文字是utf-8编码的,这样浏览器打开即会出现html乱码,反之也会出现乱码;
- html网页编码是gbk,而程序从数据库中调出呈现是utf-8编码的内容也会造成编码乱码;
- 浏览器不能自动检测网页编码,造成网页乱码。
解决办法:
- 使用软件编辑HTML网页内容;
- 如果网页设置编码是gbk,而数据库储存数据编码格式是UTF-8,此时需要程序查询数据库数据显示数据前进程序转码;
- 如果浏览器浏览时候出现网页乱码,在浏览器中找到转换编码的菜单进行转换。
浏览器同源策略、跨域
同源策略(Same Origin Policy)是浏览器最基本最核心的一个安全功能。帮助阻隔恶意文档,防止恶意网站获取其他网站的本地数据(cookie、localStroage)、DOM(iframe)以及axiox请求。
同源是指:同协议、同域名、同端口号
当两个网站 或者 前端页面地址和后端服务器 不同源时,交互操作获取资源等的时候就会报跨域的错误。
这里要提到一点,所有具有src属性的HTML标签都是可以跨域的,<script>、<link>、<iframe>、<img> 、<video>等标签可以实现跨域加载资源,方式相当于GET。
但浏览器对于这种方式加载到的资源做了限制,只能做脚本执行、样式应用等操作,所以如果你在网页展示一张跨域的图片是可以的,但是放在canvas中就会报跨域错误了,这时解决方案是添加`crossorigin`属性。
还有一些API也允许跨源访问,例如iframe.contentWindow,window.parent、window.open、window.opener以及H5的新特性:window.postMessage
那么我们如何进行不同源的交互呢?就是跨域CORS, 全称:跨源资源共享(Cross-Origin Resource Sharing)
跨域CORS
对于开发者来说,解决跨域有两种,其中一个是JSONP,另一种是修改服务器设置
JSONP是非XHR请求,是利用script标签请求资源没有跨域限制的原理。实现方式是
- 在请求地址中拼接一个回调函数(第一步我们定义的函数),得到一个新的地址,将这个新地址赋值给
JSONP只能进行GET请求,而且需要后端配合。而且因为绕过了同源策略所以会造成安全问题。
CORS
在HTTP1.1协议中,请求方法有两类,简单请求和非简单请求
- 简单请求:浏览器先发送请求,再判断是否跨域,请求方法有:GET、POST、HEAD,请求类型Content-Type为:text/plain、multipart/form-data、application/x-www-form-urlencoded。
- 非简单请求:浏览器先发送预检命令(OPTIONS方法),检查通过后才发送真正的数据请求。PUT、DELETE是非简单请求。
应用服务器处理
- 带有cookie的跨域:需要指明允许跨域请求的调用方域名,Access-Control-Allow-Methods 的值为调用方域名。
- 不带有cookie:设置“Access-Control-Allow-Origin”: “*”, 即允许跨域的原始域名为任意域名。
HTTP 服务器处理
在真正的应用场景中,我们与后端应用服务器交互的过程中,都会经过HTTP服务器中转,这里对常用的Nginx服务器进行说明
- 在Nginx服务器对请求做处理,在响应头中添加Access-Control-Allow-Methods *;
server{
listen 80;
server_name b.com;
location /{
proxy_pass http://locahost:8080/;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Orgin $http_origin;
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
if ($request_method === OPTIONS) {
return 200;
}
}
}
- 反向代理
server{
listen 80;
server_name a.com;
location /{
proxy_pass http://locahost:8080/;
}
# 将上面的客户端请求地址代理到a.com下的/axioss下
location /axioss{
proxy_pass http://locahost:9090/test;
}
}
浏览器事件流向
常见浏览器所用内核
- Trident内核:代表作品是IE,因IE捆绑在Windows中,所以占有极高的份额,又称为IE内核或MSHTML,此内核只能用于Windows平台,且不是开源的。
- Gecko内核:代表作品是Firefox,即火狐浏览器。因火狐是最多的用户,故常被称为firefox内核它是开源的,最大优势是跨平台,在Microsoft Windows、Linux、MacOs X等主要操作系统中使用。
- Webkit内核:代表作品是Safari、曾经的Chrome,是开源的项目。
- Presto内核:代表作品是Opera,Presto是由Opera Software开发的浏览器排版引擎,它是世界公认最快的渲染速度的引擎。在13年之后,Opera宣布加入谷歌阵营,弃用了Presto。
- Blink内核:由Google和Opera Software开发的浏览器排版引擎,2013年4月发布。现在Chrome内核是Blink。谷歌还开发了自己的JS引擎,V8,使JS运行速度极大地提高了
node和浏览器环境有什么区别
- node和浏览器都是JS的运行平台,不同浏览器的JS引擎不同,就会有兼容性问题,而NodeJS是基于Chrome v8引擎进行封装,所以不会有兼容性问题。
- JS在浏览器中主要用于用户的交互效果,可以进行DOM和BOM的操作;而Node像其他的服务端技术一样,支持文件读写、模块加载等特性。
- Node使用的CommonJs的模块标准require(),浏览器使用的是ES的模块标准import。
- 浏览器下this指向window,node中this指向global。
- Node中,microtask在事件循环的各个阶段之间执行;浏览器端,microtask在事件循环的macrotask执行完之后执行。
浏览器本地存储方式及使用场景
Cookie、LocalStorage、SessionStorage区别
前端储存的方式有哪些