likes
comments
collection
share

深入浅出之浏览器(前端视角)

作者站长头像
站长
· 阅读数 15

1.组成部分

1.用户界面(User Interface)

用户界面主要包括工具栏、地址栏、前进/后退按钮、书签菜单、可视化页面加载进度、智能下载 处理、首选项、打印等。除了浏览器主窗口显示请求的页面之外,其他显示的部分都属于用户界面。

2.渲染引擎(Rendering Engine)

主要用来渲染静态资源(html,css,图片等),能够准确计算页面布局,可使用“回流”,“重绘”逐步调整页面元素的位置(例如chrome 浏览器的 blink ,老版本的 webkit)。

3.Js引擎(JavaScript Interpreter)

以v8引擎为例,先读取js源代码,使用parse解析器将JavaScript代码转换成AST(抽象语法树),再通过Ignition解释器,会将AST转换成ByteCode(字节码)输出。

4.网络(Networking)

用来完成网络调用或资源下载的模块,基于TCP协议,如HTTP,Websocket。(太长,后续会细说)

6.显示后端(Display Backend)

显示后端提供绘图和窗口原语,包括:用户界面控件集合、字体集合。

7.数据持久层(Data Persistence)

管理用户数据,例如书签、cookie和偏好设置等。(太长,后续会细说)

2.加载资源顺序及流程

首先我们需要了解有哪些资源:

  • html
  • css
  • js
  • 字体
  • 图片
  • ajax(api接口请求)
  • ...

其次需要理解加载流程:这里比较重要也是比较绕的点需要着重挑出来,就是dom,css,js的顺序,分三大类情况讨论:

1.dom与css

外链样式:两者的加载是异步进行,互不干扰的不过CSS的加载会阻塞DOM的渲染,因为必须等DOM树和CSSOM树合并为渲染树之后,才能进行DOM的渲染。(这里需要着重区分渲染和加载的区别)行内样式与内联样式:行内样式是dom的某个节点的style属性,内联样式是以style标签包裹的形式,那么前者是节点,后者是标签,他们会被当做dom的一部分随着dom的加载而加载。

2.dom与js

外链js:当加载js文件时,会阻塞dom的加载,如果加载js时间过久,会导致页面显示滞后,出现“假死”状态,如果你在head内外链引入js,在js中操作到dom,就会获取不到dom出现异常,这也是为什么我们一般会将js代码写在onload钩子中,为什么一般将js引入放在文档的最后位置。(题外话:defer,async属性可以人为控制这种情况)内联js:按从上到下按顺序加载,读到script标签包裹的js代码,立即执行,执行期间,阻塞dom加载,执行完,继续向下加载。

3.css与js

有一种情况格外注意,在JS中访问了CSSDOM中某个元素的样式,那么这时候就需要等待这个样式被加载完成才能继续往下执行JS脚本,当然反之,这两者是互不影响的。

总结:

html>css>font>图片js>ajax>prefetch(预加载资源)(一般情况,也可以人为控制顺序)

3.工作原理

1.拿到url,DNS服务器域名解析成ip地址2.建立TCP连接,三次握手

第一次握手:客户端应用进程主动打开,并向服务器端发送请求(即:发送端发送一个带有SYN标记的数据包给接收端)
第二次握手:服务端应用进程被动打开,若同意客户端的请求,则返回一个确认报文(即:接收端接收到数据包之后,回传一个带有SYN/ACK标记的数据包给发送端表示确认)
第三次握手:客户端接收到确认报文后,通知上层应用进程连接已经建立,并向服务器发送一个确认报文。服务器接收到客户端的请求也通知其上层应用进程连接已建立。(最后,发送端再回传一个带有ACK标记的数据包给接收端,代表“握手”’结束)

3.请求静态资源,并按(2.加载资源顺序及流程)规则加载,dom树与css规则树整合成rendertree,layout布局完成后,ui后端绘制到页面上(加载和渲染是同时进行的),遇到js的解析,由js解析引擎完成(这里涉及到js的事件循环机制以及单线程,详见下面的js的执行机制)。4.断开http连接,四次挥手

第一次挥手:客户端的应用程序发出连接释放的报文,并停止发送数据。
第二次挥手:服务端接收到连接释放的报文后,发出确认报文。此时连接处于半封闭状态,客户端不再继续向服务端发送数据,但是服务端继续向客户端发送数据。
第三次挥手:若服务端没有了要向客户端发送的数据,其应用进程会通知服务器释放TCP连接
第四次挥手:客户端接收到释放报文后,必须确认。再经过2MSL(最长报文寿命)s后,本次TCP 连接正式结束。

4.回流和重绘以及如何避免

重绘:在渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格,不影响布局。场景:元素的显隐;元素的尺寸发生变化(包括外边距、内边距、边框大小、高度和宽度等);内容发生变化;页面首次渲染;浏览器窗口尺寸发生变化回流:在渲染树中的一部分(或者全部)因为元素的规模尺寸、布局 、显隐等改变而需要重新构建场景:背景色,字体颜色等不改变结构布局的样式改变如何避免:1.使用碎片文档documenFragment,多次合并一次渲染2.避免直接修改样式,修改类名3.脱离文档流当然目前大多数浏览器都进行了自身的优化--渲染队列,浏览器会将所有的回流,重绘放到一个队列中,将达到一定数量或一定间隔时间浏览器会对队列批处理,这样的目的是多次变一次,减少重绘,回流的次数。

5.js的执行机制

js是单线程的,且自上而下解析执行的,当遇到同步任务,就放到主线程中正常按顺序执行,当遇到异步任务,则放到event table中,并将其一对一映射为函数,当满足触发条件后,被推入event queue,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中,js的执行机制也叫事件循环。多个异步任务的执行顺序如何判断:异步任务分为宏任务和微任务宏任务:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)。微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)。先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。

转载自:https://segmentfault.com/a/1190000043494470
评论
请登录