开篇前的碎碎念。最近看了系列的文章,觉得很不错。所以打算趁此机会,整理一下~主要是以chromium为例。因为内容太多,某些点不会特别详细的讨论,可以参考其他文。
流程如下图:

文字描述过程:

- 地址栏中输入地址:www.google.com。 一般来说,新打开一个tab页,浏览器会新开启一个进程(例外:多个空白的tab页会合并,只存在一个进程) (浏览器多进程的更多内容见下文)
-
浏览器检查缓存中的DNS记录,以查找www.google.com的相应IP地址。
(1)首先,检查浏览器缓存。浏览器为您之前访问过的网站维护一段固定时间的DNS记录存储库。因此,它是第一个运行DNS查询的地方。
(2)其次,检查操作系统缓存。如果在浏览器缓存中找不到它,浏览器将在底层计算机操作系统上进行系统调用(即Windows上的gethostname)以获取记录,因为操作系统还维护DNS记录缓存。
(3)第三,检查路由器缓存。如果在您的计算机上找不到它,浏览器将与维护其自己的DNS记录缓存的路由器通信。
(4)第四,检查ISP缓存。如果所有步骤都失败,浏览器将转移到ISP。您的ISP维护其自己的DNS服务器,该服务器包括DNS记录的缓存,浏览器将检查该记录,最后希望找到您请求的URL。
如果请求的URL不在缓存中,ISP的DNS服务器将启动DNS查询以查找托管www.baidu.com的服务器的IP地址(DNS查询有递归查询和迭代查询两种方式。具体的解析过程后续补充) - 获取到ip地址后,浏览器启动与服务器的TCP连接。
一旦浏览器收到正确的IP地址,它将与服务器建立连接,匹配IP地址以传输信息。浏览器使用互联网协议来构建此类连接。
可以使用许多不同的互联网协议(例如:直播中使用较多的UDP协议,邮件系统使用的SMTP协议,文件传输使用的FTP协议),但TCP是用于任何类型的HTTP请求的最常用协议。
为了在计算机(客户端)和服务器之间传输数据包,建立TCP连接很重要。使用称为TCP / IP三次握手的过程建立此连接。这是一个三步过程,客户端和服务器交换SYN(同步)和ACK(确认)消息以建立连接。
(1)客户机通过互联网向服务器发送SYN数据包,询问它是否为新连接打开。
(2)如果服务器具有可以接受和发起新连接的开放端口,则它将使用SYN / ACK数据包响应SYN数据包的确认。
(3)客户端将从服务器接收SYN / ACK数据包,并通过发送ACK数据包来确认它。
然后建立TCP连接以进行数据传输! - 浏览器向Web服务器发送HTTP请求。
- 服务器处理请求并发回响应。
- 服务器发出HTTP响应。 服务器响应包含您请求的网页以及状态代码,压缩类型(Content-Encoding),如何缓存页面(Cache-Control),要设置的任何cookie,隐私信息等。 使用数字代码详细说明了五种类型的状态。 ● 1xx仅表示信息性消息 ● 2xx表示某种成功 ● 3xx将客户端重定向到另一个URL ● 4xx表示客户端出错 ● 5xx表示服务器部分出错
- 浏览器获取到请求的html资源之后,浏览器内核(渲染引擎)解析html。
浏览器解析过程(浏览器解析过程后面会详细介绍):html解释器解析html结构成dom tree; css解析css成style tree,dom tree和css tree合并成渲染树,再布局(定位元素的位置),painting。 在这个过程中,如果遇到了外链资源,图片等资源的话会交给brower进程开启资源下载线程;而如果是js的话,并且没有声明是异步。GUI渲染线程便会挂起,js引擎线程执行。 —
专用碎碎念:
在第二个步骤,直接就去获取ip了,实际上一般在web开发中,对于js,css,图片等资源都会采取缓存机制的,毕竟每个资源都走http协议,用户表示很焦灼!
DNS(Domain Name System)
DNS主要是将我们便于记忆的主机名转换成对应的32比特的ip地址。
DNS工作原理:
DNS采用的是分布式、层次结构。层次结构有根DNS域名服务器(目前全球有13个),顶级DNS域名服务器(eg:com、cn),权威DNS域名服务器。另外一个不属于该层次但是也是极其重要的一个,本地DNS域名服务器。
(在不考虑DNS服务器存在DNS缓存的情况下)
首先,主机A需要知道服务器B的ip地址,那么它会先对本地DNS域名服务器发送请求。
其次,本地DNS域名服务器接受到请求之后,便向根DNS域名服务器发出DNS查询请求。
然后,根DNS域名服务器将对应的顶级DNS域名服务器的ip地址返回给本地域名服务器。
此后,那么本地域名服务器收到回复之后,再向返回的顶级DNS域名服务器发送请求。
最后,顶级DNS域名服务器返回对应的ip地址响应报文。
下图以www.baidu.com作为例子:
其中:
Type=A时,Name=主机名,Value=ip
Type=NS时,Name=域,Value=知道该如何获取该域中主机ip地址的权威DNS服务器主机名
Type=CNAME时,Name=别名,Value=别名为Name的主机的规范名
Type=MX时,Name=别名,Value=别名为Name的邮件服务器的规范主机名
为了减少查询,DNS采用了缓存机制,在收到一个DNS请求报文时,会先查询本地服务器是否存在,如果有便直接返回; 没有的话再去相应的DNS服务器请求。当请求的服务器返回响应报文后,会在自己的服务器上保存一份, 因为IP地址和域名对应是变化的,所以缓存会有相应的时效限制。
计算机网络五层协议

应用层
应用层协议主要是对数据格式进行规范。目前存在的协议有Web服务的HTTP协议,文件传输的FTP协议,邮件发送的SMTP协议,以及服务于HTTP这些的DNS协议。
HTTP协议
HTTP定义了客户端和服务器端发送的报文格式。
1、采用的传输层协议是可靠数据传送,面向连接的TCP传输协议
2、HTTP无状态连接,但是可以通过cookie来标识用户
- Cookie是Web服务器发送到浏览器的数据字符串。 当浏览器在将来请求来自同一个域的对象时,该浏览器会将同一日期字符串发送回原始服务器。 该数据以一个称为“Set-Cookie”的HTTP报头格式从Web服务器发出。浏览器以称为“Cookie”的HTTP 报头格式将Cookie 发送回服务器。
- Cookie分为两种:会话型Cookie和持续性Cookie。 会话型Cookie:这些Cookie只保存在内存中,当浏览器退出即清除这些Cookie。该类型Cookie没有设置有效期; 持久性Cookie:当浏览器退出仍然保留Cookie的内容。该类型Cookie有一个有效期。 3、有请求报文和响应报文两种
- 请求报文 请求报文包含:请求方法(get,post…),请求协议,请求路径,首部体和实体
- 响应报文
响应报文包含:协议,状态码,状态码相应描述,首部体,实体
- HTTP 2.0 HTTP/2.0 相比1.0有哪些重大改进?
专用碎碎念:
这部分算是计算机网络相关的范畴了,在前端开发中,我们接触的最多的协议便是HTTP协议。要说应用层和前端的联系:
-
我们通过响应code判断前端错误还是服务端错误。 一般简单的来说,4xx一般都是前端问题,5xx一般是服务端的问题(一般一般)。 就前端产生错误的原因很多,404:地址错误,405:请求的方法不被允许,403:服务端接收到请求,但是拒绝了……
而每个可能产生的原因并不单一,简单说405,请求方法不被允许,深层的原因可能是你的请求属于非简单请求,浏览器发起了方法为options的预检请求。 -
http协议是无状态的,但是有些时候需要获取用户的状态,一般通过cookie来标志。跨域时cookie如何设置?path?domain?expires? 为什么我明明设置了cookie但是请求没有带过去等等?
-
http协议中各字段含义?推荐《HTTP图解》,使用图示说明HTTP协议中字段的含义。没事可把玩。
-
http服务一般运行在80端口,https运行在443端口。
FTP
FTP(File Transfer Protocol:文件传输协议)作为文件传输的协议。
- 采用TCP作为传输协议。
- 与HTTP区别:采用两个并行的TCP连接来传输文件,其中一个是控制连接,一个是数据连接。
专用碎碎念:
要是对FTP感兴趣,查维基百科,比我这个详尽。我是一个点到为止的咸鱼!
SMTP
因特网电子邮件应用协议
应用层思维导图

==传输层==
==网络层==
==链路层==
==实体层==
浏览器多进程
多进程优势:
- 一个Web应用程序中的呈现引擎崩溃不会影响浏览器或其他Web应用程序
- 操作系统可以并行运行Web应用程序以提高其响应速度
- 可以在限制性沙箱中运行渲染引擎进程,以便在漏洞发生时帮助限制损坏
下图是chrome的任务管理器内容:

从上面可以看出浏览器部分进程:
- Brower进程(主进程):只有一个浏览器进程,用于管理浏览器的选项卡,窗口和“chrome”。此过程还处理与磁盘,网络,用户输入和显示的所有交互,但它不会尝试从Web解析或呈现任何内容。
- GPU进程:只有一个。可以向图形驱动程序发出GPU加速操作
- 渲染进程(浏览器内核):浏览器进程创建许多渲染器进程,每个进程负责呈现网页。渲染器进程包含处理HTML,JavaScript,CSS,图像等的所有复杂逻辑。
- 插件进程:浏览器的扩展插件
下图是使用chrome trace获取的chrome的主要进程:
通过chrome trace可以追踪浏览器内核中线程情况,对于chrome的多线程会有一个比较直观清楚的感受,因本人技术有限,暂没有理清。
专用碎碎念
曾几何时,大概是刚接触电脑的远古时期,还是那种一个页面卡导致整个浏览器奔溃。
浏览器渲染
每个进程下面存在一个或者多个线程,这里我们着重的讲渲染进程(浏览器内核)存在的一些主要的线程:
- JS引擎线程:负责处理Javascript脚本程序。只有一个(js是单线程——),与GUI渲染线程互斥
- GUI渲染线程:负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。与JS引擎线程互斥
- 事件触发线程:归属于浏览器而不是JS引擎,用来控制事件循环
- 定时触发器线程:浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
- 异步HTTP请求线程
- 解释器线程:解释成机器可理解的
- 垃圾收集器线程:删除未连接的(不再可从ROOT节点引用))JavaScript对象使用标记和扫描算法释放内存 ……
浏览器渲染过程简述如下:
- 处理 HTML 标记并构建 DOM 树。
- 处理 CSS 标记并构建 CSSOM 树。
- 将 DOM 与 CSSOM 合并成一个渲染树。
- 根据渲染树来布局,以计算每个节点的几何信息。
- 将各个节点绘制到屏幕上。
以色列Tali Garsiel开发人员详细的写过HTML和CSS解析过程,涉及到与编译原理相关的内容(我学的编译原理早就还给老师了~),有兴趣的可以看看相关的内容:How Browsers Work: Behind the scenes of modern web browsers
专用碎碎念
Q1: 为什么js不能像Java那样多线程呢?emmmmmmmm你能想象同时修改DOM带来的混乱不可控性吗?
Q2: web wordker出现了,所以js现在是多线程了吗?还是单线程。web wordker归主线程管,帮助我们处理一些复杂的计算,但是限制也多,是不可直接操作DOM元素的。
js渲染引擎
event loop 是否曾看见下面这样的一段代码:
setTimeout(fn,0)
设置一个等待0s的定时器。是否进一步的思考这样做的意义呢? 下面我们从这里入口谈一谈js引擎中的事件循环机制。
JavaScript不似java之类的其他语言,它是一个单线程。 我们需要了解下面的概念:
- 执行栈:当前执行的主线程(唯一的)
- 任务队列:等待执行的异步队列——避免线程一直等待阻塞。
- macrotask(宏任务):UI render,setTimeout,script(主代码块),事件监听等
- microtask(微任务):promise,MutationObserver等
下面简单描述event loop过程:
- 从script(主代码块)开始。
- 遇到同步任务便推入执行栈立即执行。
- 如果遇到异步任务便会推入异步队列。宏任务的话,推入宏任务队列;微任务的话,就推入微任务队列。
- 等待没有同步任务执行,执行栈为空时。便先去执行微任务队列,将可以执行的微任务推入执行栈执行。
- 可执行的微任务执行完之后,便检测是否需要ui render。有的话,执行ui render。(注意,同步任务中有需要ui render并不是理解执行,而是等到微任务执行完之后才会执行)
- 最后再执行宏任务队列中的可执行任务
- 以此循环
下面的图更直观的表述这一过程:

其中很早以前罗伯滋就为我们模拟了这一过程(因为事件就,所以呢,没有实现有微任务的过程),帮助我们更加清晰的理解这一过程。 event loop(需翻墙)
最后我们下面以一个例子来说明:
setTimeout(function() {
console.log('timeout1');
})
new Promise(function(resolve) {
console.log('promise1');
for(var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('promise2');
}).then(function() {
console.log('then1');
})
console.log('global1');
//promise1
//promise2
//global1
//then1
//timeout1
扩展补充: location.href不是立即跳转,会将主线程的同步任务执行完之后,再把异步队列中的微任务执行完再跳转,不一定会等待执行宏任务(eg:setTimeout)。
window.location.href="https://www.baidu.com";
alert("测试");
setTimeout(function(){
alert("宏任务")
},60000);
let count=0;
test();
function test(){
let promise=new Promise(function(resolve,reject) {
alert("微任务");
resolve();
});
promise.then(function () {
count++;
alert("微任务回调"+count);
if(count<10){
test();
}
})
}
专用碎碎念
我查阅了众多的资料,想知道任务队列为什么要设置微任务和宏任务这两种。但是都没有查到关于这方面的比较确切的描述。
所以姑且区分微任务与宏任务,是因为处理渲染时机(微任务执行完之后,会检查是否需要ui render),没有依据的说法,别信我!
==CSS==
CSS类似于可视化模型高级部分,我自己都没有研究……
谈性能优化
根据浏览器渲染的特点,从代码层面谈谈页面的性能优化问题。 我们知道
- UI渲染线程和JS线程是互斥的,当遇到script标签,会暂停文档的解析,执行js代码,所以?
- 尽量把script标签置于body底部,或者添加defer属性,告知延迟等页面加载完成后再加载脚本
- setIntervar定时一段时间连续执行的定时器,而在大多数情况下,我们并不知道定时执行的函数是什么时候执行完。就会容易出现,下一次定时已经开始了,但是上一次的函数还没有执行完。这种时候,event loop是直接将事件推入队列,造成可能一次性等情况
- 尽量使用setTimeout;如果是动画的话,可以使用requestAnimationFrame ……
其他
web worker 我们都知道js是单线程,但是新增加的H5特性web worker为Web内容在后台线程中运行脚本提供可能,线程可以执行任务而不干扰用户界面。 那么web worker是js多线程的实现吗? 事实上web worker是属于主进程管理而多开启的一个线程,并不是实现js多线程。 我们可以通过web worker进行一些复杂的计算,但是同样会存在以下的限制。 ……
==内存==
延伸内容
跨域
跨域产生原因
说到跨域,就一定要说到浏览器的同源策略。 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。这个只是浏览器的限制 其中同源必须要:协议,域名,端口都相同。
而大多时候我们都不能保证请求的页面和请求的地址同源,所以就需要解决跨域问题。 一般解决跨域的常见几种方式是:jsonp,CORS(Cross-origin resource sharing),代理。下面将分别介绍这几种跨域方式。
跨域解决方式
- jsonp jsonp的原理主要是script标签(我们可以随意加载其他域的js脚本不受限制),在查询参数跟上callBack(具体是什么参数名和服务端协定)参数,callBack的值是定义在全局对象下面的函数方法名。 形如下面的形式:
<script src="xxxx?callBack=callMethod"></script>
请求成功之后,返回js可执行的函数代码,会直接调用callBack值的方法,并将json字符串通过参数传给函数。 jsonp只适用于get请求。
其他: ajax,fetch-jsonp等都对jsonp进行了封装。
- CORS CORS即为跨域资源共享。这个一般是服务器需要在response中设置响应头。
Access-Control-Allow-Origin:*
Access-Control-Allow-Method:GET //请求方法
-
代理 最开始我们便说了同源策略是浏览器的限制,服务端并没有这种限制。 所以我们可以通过代理的方式来实现。 数据流程: 数据请求过程:浏览器-》代理服务器-》目标服务器 数据返回过程:目标服务器-》代理服务器-》浏览器
-
postMessage
web安全
从请求页面到页面加载这个过程中,存在很多的安全性问题。下面将一步步讨论过程中出现的一些常见的安全性问题及其解决方式。 安全
==weex==
全文基本介绍的是浏览器的一些内部结构等,而现在随着移动端的发展,追求高的开发效率&快的页面打开速度&跨平台。衍生了H5和android混合式开发。
然后,我并没有看过,看了再说吧~
参考资料
- Understanding about:tracing results
- 网络协议概述:物理层、连接层、网络层、传输层、应用层详解
- 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
- What Happens When Your Browser Requests a Web Page?
- What are the series of steps that happen when a URL is requested from the address field of a browser?
- chrome blog
(黄色批注部分表示尚未完成)