转自---> 前端食堂
这是一道十分常见的面试题
流程
1、用户在地址栏输入内容后,地址栏会将输入的内容进行合成 URL。
2、当用户输入完内容并按下回车键时,浏览器会在当前页面执行 beforeunload 事件,你可以在这个钩子中询问是否要离开当前页面,常见于一些表单提交的场景。
3、接下来开始导航流程,浏览器进入加载状态。
4、浏览器的网络进程会先查找缓存中是否存在该资源,有的话直接返回,如果没有的话会发起 URL 请求。
: 浏览器获取了这个url,当然就去解析了,它先去缓存当中看看有没有,从 浏览器缓存-系统缓存-路由器缓存 当中查看,如果有从缓存当中显示页面,然后没有那就进行步骤5;
缓存就是把你之前访问的web资源,比如一些js,css,图片什么的保存在你本机的内存或者磁盘当中。
在chrome浏览器中输入网址: chrome://chrome-urls/ chrome-urls是一个看到所有的Chrome支持的伪RUL,找到其中的chrome://appcache-internals/ 可以看见chrome的本地缓存地址:Instances in: C:\Users\User\AppData\Local\Google\Chrome\User Data\Default (0)5、接下来首先要进行的是 DNS 解析,获得请求域名的服务器的 IP 地址(这个过程我也画了一张图,放在下文),如果协议是 HTTPS,还需要建立 TLS 连接。
6、接着利用目标服务器的 IP 地址建立 TCP 连接(三次握手),构建 HTTP 请求报文,发起请求。服务器收到请求后,会根据请求信息生成响应报文。
7、浏览器的网络进程接收到响应报文后进行解析,如果状态码是 301 或者 302(还有 300、303 等 3xx 的状态码),则需要取得响应头中的 Location 对应的地址进行重定向,再重新发起请求。
8、如果状态码是 200,浏览器会根据响应头中的 Content-Type 字段来识别返回的响应体数据类型,从而进行不同的流程。如 text/html 代表 html 格式, application/octet-stream 代表字节流类型,浏览器会按照下载类型来处理。
9、如果是 HTML,浏览器会遵循 process-per-site-instance 默认策略准备渲染进程,准备好后就提交文档(将网络进程接收到的数据提交给渲染进程)。文档被提交后,渲染进程便开始进行页面解析和子资源的加载。
process-per-site-instance 默认策略:每个标签对应一个渲染进程,如果从一个页面打开了一个新页面,新打开的页面与当前页面还属于同一个站点的话,那么新页面会复用当前页面的渲染进程。
浏览器的网络进程会先查找缓存中是否存在该资源,有的话直接返回,如果没有的话会发起 URL 请求
目前 Chrome 的浏览器包括如下进程:
- 1 个浏览器(Browser)主进程
- 1 个 GPU 进程
- 1 个网络(NetWork)进程
- 多个渲染进程(运行在沙箱模式下)
- 多个插件进程
HTML渲染流程
在上图中一并画了出来,需要经过以下几个阶段:构建 DOM 树--->样式计算--->布局--->分层--->绘制--->分块--->光栅化--->合成
DNS
DNS 的解析是一个递归流程,顺序如下图中数字标记所示:
- 根 DNS 服务器:返回顶级域 DNS 服务器的 IP 地址。
扯一句:世界上有13台根服务器,而有N台根镜像服务器,所以有些国外地址会被墙 ~~~,说明域名解析第一步并不是走的根服务器,而是由根镜像服务器控制住做了过滤,有选择性地进行了域名解析,所以如果根域名服务器出了问题,我觉得我们国内仍然可以做到正常访问,当然只是新更新的域名无法访问。 - 顶级 DNS 服务器:返回权威 DNS 服务器的 IP 地址
- 权威 DNS 服务器:返回相应主机的 IP 地址
JS 栈 垃圾数据回收
- 示例代码1:
function hello () {
let name = '数据'
}
hello()
console.log(name) // Uncaught ReferenceError: name is not defined 在执行上下文时,存放在栈内存中的name地址和数据【内存块】已经回收掉了,所以在全局上下文中是访问不到。
- 示例代码2:
function hello () {
let name = '前端食堂'
let food = { name: '回锅肉' }
function world () {
var description = { slogan: '吃好每一顿饭' }
}
world()
}
hello()
上面的代码所对应的内存堆栈空间如下图所示:
栈中的垃圾回收比较简单,当一个函数执行结束后,JavaScript 引擎会通过向下移动 ESP 来销毁函数调用栈中所保存的执行上下文,ESP 就是记录当前执行状态的指针。
- 垃圾回收操作会暂停 JavaScript 的运行,回收完毕后才会恢复执行,这种行为就是全停顿。
为了降低全停顿所带来的卡顿,V8 引擎采用了增量标记(Incremental Marking) 算法进行优化,将标记过程分为一个个小任务,这些小任务的执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,这样就不会有明显的卡顿了。
当然,V8 所采用的优化方案不只这一种,而是多种方案综合使用的,除了增量回收还有并行回收、并发回收等。
并行回收:垃圾回收器会使用多个辅助线程来并行执行垃圾回收
并发回收:回收线程在执行 JavaScript 的过程中,辅助线程在后台执行垃圾回收
如果你了解【** React 的 Concurrent 模式中时间切片的原理 **】,它的实现思想是不是与增量标记算法有异曲同工之妙呢。