浏览器原理

一、首先需明确一个概念

浏览器是多进程多线程的,js是单线程的
在这里插入图片描述

二、浏览器进程

Browser进程(主进程)
  • 负责浏览器界面显示,与用户交互。如前进,后退等
  • 负责各个页面的管理,创建和销毁其他进程
  • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
  • 网络资源的管理,下载等
第三方插件进程
  • 每种类型的插件对应一个进程,仅当使用该插件时才创建
GPU进程
  • 最多一个,用于3D绘制,硬件加速
浏览器渲染进程
  • (浏览器内核)(Renderer进程,内部是多线程的):默认每个Tab页面一个进程,互不影响。主要作用为页面渲染,脚本执行,事件处理等

三、浏览器线程

GUI渲染线程
  • 负责渲染浏览器界面,解析HTML,CSS生成dom树和css规则树,然后合并生成渲染树,布局和绘制等。
  • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
  • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
JS引擎线程
  • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
  • JS引擎线程负责解析Javascript脚本,运行代码。
  • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
  • 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
事件触发线程
  • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
  • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
  • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
定时触发器线程
  • 传说中的setInterval与setTimeout所在线程
  • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
  • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
  • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms,setInterval低于10ms会调整为10ms。
异步http请求线程
  • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
  • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

四、常见问题

浏览器渲染过程
  1. 输入地址,browser进程获取请求,然后通过网络请求获取页面内容
  2. 获取到页面内容后browser通过RenderHost的接口传递到Render进程
  3. Render进程将获取的内容简单解析后交给渲染线程开始渲染
  4. 最后会browser进程会将render进程返回结果绘制在用户界面上
3中渲染线程做了啥

渲染流程

  1. 解析html生成DOM树
  2. 解析css生成CSSOM树
  3. DOM和CSSOM生成渲染树(渲染树只会显示需要显示的节点和节点信息,如果某个节点是display: none则不会显示)
  4. 生成布局(也叫回流),即将所有渲染树的所有节点进行平面合成(明确每个节点的位置和大小)
  5. 将布局绘制(paint)到屏幕上(布局完成后浏览器会立即发出“Paint setup“ 和 ”Paint”事件)
构建DOM

浏览器会遵守一套步骤将HTML 文件转换为 DOM 树。宏观上,可以分为几个步骤:
构建DOM流程

  1. 浏览器从磁盘或网络读取html的原始字节,并根据文件的指定编码(UTF-8)转换成字符串
  2. 将字符串转换成token,例如<html>,<head>
  3. 生成节点对象并构建成DOM
渲染过程中遇到js怎么处理

JavaScript的加载、解析与执行会阻塞DOM的构建。如果引入了JavaScript,CSSOM会阻塞DOM的构建(但不会阻塞DOM的解析),原本DOM和CSSOM的构建是互不影响的,但是因为js不仅会
进行dom的操作,还会对样式进行修改,而不完整的CSSOM是无法使用的,所以当js遇到操作CSS的时候,js会阻塞等待CSSOM的构建完成,而js又阻塞了DOM的构建,所以CSSOM变相的阻塞了DOM的构建

浏览器地址栏输入URL,按下回车后究竟发生了什么?
  1. 地址栏输入地址回车
  2. 解析域名或者ip地址(浏览器缓存、系统缓存、路由缓存、递归查询、迭代查询)
  3. 与服务器建立tcp连接
  4. 浏览器发送请求(可能会有重定向)
  5. 服务器解析请求,并产生HTML响应
  6. 释放tcp连接
  7. 浏览器显示页面
  8. 发送其他嵌入在html的其他请求
重排(回流)和重绘

回流必定会发生重绘,重绘不一定会引发回流
元素布局、尺寸、隐藏等改变会发生回流
元素外观、风格等不影响布局的改变会发生重绘

async和defer的作用是什么?有什么区别?

async和defer
其中蓝色线代表JavaScript加载;红色线代表JavaScript执行;绿色线代表 HTML 解析。

  1. 无async和defer:js的加载和执行会阻塞HTML的解析
  2. async(异步加载):js加载不会阻塞HTML的解析,js加载完之后会立即执行,这时会阻塞HTML的解析
  3. defer(延迟执行):js加载不会阻塞HTML的解析,js的执行会放到HTML解析完之后
DomContentLoaded 和 load的触发
  • DOMContentLoaded 事件在 html文档加载完毕,并且 html 所引用的内联 js、以及外链 js 的同步代码都执行完毕后触发
  • 当页面 DOM 结构中的 js、css、图片,以及 js 异步加载的 js、css 、图片都加载完成之后,才会触发 load 事件
为什么操作dom慢

因为DOM是渲染引擎的东西,而js是js引擎的东西,我们操作DOM的时候必然会涉及两个线程之间的通信,会带来一些性能的损耗,而且操作dom还可能会带来回流重绘的情况,也回影响性能

优化渲染效率的建议
  1. 样式文件应当在 head 标签中,而脚本文件在 body 结束前,这样可以防止阻塞的方式。
  2. 简化并优化CSS选择器,尽量将嵌套层减少到最小。
  3. 尽量减少在 JavaScript 中进行DOM操作
  4. 修改元素样式时,更改其class属性是性能最高的方法
  5. 尽量用 transform 来做形变和位移
强缓存和协商缓存

判断是什么缓存是通过去看response header的cache-control得出的,cache-control的值不同表示不同的缓存策略

  1. cache-control: max-age=xxxx,public
    客户端和代理服务器都可以缓存该资源;客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,如果用户做了刷新操作,就向服务器发起http请求
  2. cache-control: max-age=xxxx,private
    只让客户端可以缓存该资源;代理服务器不缓存。客户端在xxx秒内直接读取缓存,statu code:200
  3. cache-control: max-age=xxxx,immutable
    客户端在xxx秒的有效期内,如果有请求该资源的需求的话就直接读取缓存,statu code:200 ,即使用户做了刷新操作,也不向服务器发起http请求
  4. cache-control: no-cache
    跳过设置强缓存,但是不妨碍设置协商缓存;一般如果你做了强缓存,只有在强缓存失效了才走协商缓存的,设置了no-cache就不会走强缓存了,每次请求都回询问服务端。
  5. cache-control: no-store
    不缓存,这个会让客户端、服务器都不缓存,也就没有所谓的强缓存、协商缓存了。

协商缓存的标识是在response header设置

etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT

etag:是一个hash,每个文件都有一个,文件改变hash就变了
last-modified:文件修改时间,精确到秒

也就是说,每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,这时候对客户端来说,每次请求都是要进行协商缓存了

协商缓存步骤总结:
请求资源时,把用户本地该资源的 etag 同时带到服务端,服务端和最新资源做对比。
如果资源没更改,返回304,浏览器读取本地缓存。
如果资源有更改,返回200,返回最新的资源。

事件相关

js是单线程的,我们的js代码分为同步和异步代码
事件队列
所有代码在js主线程中按顺序执行,当遇到了异步代码(一些可能会耗时的操作),
会将这些异步任务放到其他线程执行(事件触发线程、定时触发器线程、异步http请求线程),
当这些异步任务执行有了结果就会在事件队列存入一个事件,
当主线程中的同步代码执行完之后就会读取事件队列的结果到主线程执行
宏任务和微任务
js异步有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入事件队列,然后在执行微任务,将微任务放入事件队列;
需要注意的是,这两个事件队列不是一个队列。当你往外拿的时候先从微任务里拿这个回掉函数,然后再从宏任务的队列上拿宏任务的回掉函数
宏任务有:整体代码script,定时器任务(setTimeout,setInterval),I/O任务(网络请求、文件读写等需要进行I/O操作的任务),用户交互任务(点击、输入等),请求动画帧的任务(requestAnimationFrame()方法设置任务)
微任务有:Promise,process.nextTick

浏览器原理
深入浅出浏览器渲染原理
彻底弄懂强缓存和协商缓存
javascript的宏任务和微任务
js中的同步和异步的个人理解
JS引擎运行机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值