本文主要进行浏览器相关知识的整理总结。
一、浏览器的存储
浏览器的存储包括cookie,session,LocalStorage,sessionStorage,indexedDB。
作用 | cookie | session | sessionStorage | LocalStorage | indexedDB |
---|---|---|---|---|---|
储存时间 | 设置或不设置 | 默认30分钟 | 仅当前页面 | 永久 | 永久 |
储存大小 | 4K | 无限制 | 5M | 5M | 无限制 |
储存形式 | 字符串 | 对象 | 字符串型的键/值对 | 字符串型的键/值对 | 类似于数据库表的形式 |
储存位置 | 浏览器端 | 服务器端 | 浏览器端 | 浏览器端 | 浏览器端 |
交互通信 | 与服务器交互通信 | 不交互 | 不交互 | 不交互 | 不交互 |
cookie
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么操作,为了辨别用户身份以及实现应用程序,就需要用到cookie。因此服务器才知道用户浏览了几个页面或者买了什么东西等。服务器可以设置或读取cookies中包含信息,因此维护用户跟服务器会话中的状态。
主要包括名字、值、过期时间、路径和域。路径与域一起构成cookie的作用范围。cookie是由服务器生成然后然后发送到浏览器的。与服务器进行通信时,每次请求都会携带在HTTP请求头中。
不设置时间,将被浏览器保存在内存中,关闭浏览器窗口,cookie就会消失,这个cookie的生命周期为浏览器会话期间,这种生命周期为浏览器会话期间的cookie被称为会话cookie。
设置时间,将cookie保存在浏览器硬盘中,直到过期才消失,除非用户手动处理,不然硬盘cookie不会被删除。
session
服务器状态管理技术,将状态信息保存在服务端。当用户第一次发送请求时,服务器生成session对象,并返回一个session_id给浏览器的cookie作为value。再次请求时故居cookie也就是session_id查询对应的session对象,有就使用,无就创建session对象。
session没有大小限制,tomacat中默认的有效时间30分,当用户和服务器的交互时间超过默认时间后session就失效。
storage
localStorage和sessionStorage都是本地存储,不与服务器进行交互通信,减少不必要的请求,大小5M左右,因各浏览器的存储空间有差异
localStorage是永久保存在浏览器,除非用户主动清除。
sessionStorage仅仅存储在当前页面,关闭浏览器或关闭页面存储的数据就会销毁
indexedDB
indexedDB是浏览器提供的本地数据库,可储存大量的结构化数据(也包括文件/二进制大型对象(blobs))。IndexedDB 是一个事务型数据库系统,类似于基于 SQL 的 RDBMS。 然而,不像 RDBMS 使用固定列表,IndexedDB 是一个基于 JavaScript 的面向对象数据库。IndexedDB 允许您存储和检索用键索引的对象。使用 IndexedDB 执行的操作是异步执行的,以免阻塞应用程序。具体可以具体文档
二、浏览器的缓存
浏览器缓存
简单来说,浏览器缓存就是浏览器保存通过HTTP请求获取所有的资源。
优点:减少网络带宽消耗;降低服务器压力;减少网络延迟,加快页面打开速度。
①使用HTML Meta标签
即在HTML页面的节点中加入标签,代码如下
<meta http-equiv="Pragma" content="no-cache" >
// Pragma是http1.0版本中给客户端设定缓存方式之一
上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。但是,事实上这种兼用缓存的形式用处很有限:
仅有IE才能识别这段meta标签含义,其它主流浏览器仅识别“Cache-Control: no-store”的meta标签。
在IE中识别到该meta标签含义,并不一定会在请求字段加上Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源则不受影响)。
②使用缓存相关的HTTP消息报头
a.强缓存
浏览器发送请求前,会先根据本地缓存资源中的header中的信息判断是否命中强缓存,如果命中,则直接从缓存中读取资源,不会去请求服务器,也就是不与服务器发生交互行为。
这里的响应header中的信息指的是expires和cahe-control(优先级最高)
i.EXpire:这个时间代表着这个资源失效的时间,在此时间之前,即命中缓存。这种方式有一个明显的缺点,用于失效时间是一个绝对时间,所以当服务器与客户端时间偏差比较大的时候,就会当值缓存混乱。
ii.cahe-control:主要是利用该字段的max-age值来判断,例如 Cache-Control:max-age=3600;
ache-control属性:
max-age: 设置缓存的最大的有效时间,单位为秒(s)。max-age会覆盖掉Expires
no-cache:需要进行协商缓存,发送请求到服务器确认是否使用缓存。
no-store:禁止使用缓存,每一次都要重新请求数据。
public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。
private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。
b.协商缓存
当强缓存没有命中的时候,浏览器会发送一个请求到服务器,服务器根据 header 中的部分信息来判断是否命中缓存。如果命中,则返回 304 ,告诉浏览器资源未更新,可使用本地的缓存。
这里的 header 中的信息指的是 Last-Modify(响应)/If-Modify-Since(请求)和 ETag(响应)/If-None-Match(请求)
i.Last-Modify是一个时间标识该资源的最后修改时间。If-Modify-Since其值为上次响应头的Last-Modify值,再次向web服务器请求时带上头If-Modify-Since,则与被请求资源的最后修改时间进行对比。若最后修改时间较新,说明资源又被改动过,则响应资源内容,包括更新Last-Modify的值,HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用本地的缓存
周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是 Last-Modified 可不这样认为,因此便有了 ETag。
ii.与 Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 返回的是一个校验码。ETag 是一个资源的唯一标识符,资源变化都会导致 ETag 变化。服务器根据浏览器上送的 If-None-Match (ETag值)来判断是否命中缓存。
注:Last-Modify与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。
c.其他缓存
i.DNS缓存
全称 Domain Name System ,即域名系统。
万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。DNS协议运行在UDP协议之上,使用端口号53。
DNS解析:例如:www.name.com (域名) 通过DNS解析为 111.222.33.444 (IP地址)
DNS缓存:有dns的地方,就有缓存。浏览器、操作系统、Local DNS、根域名服务器,它们都会对DNS结果做一定程度的缓存。
查询过程:
1、首先搜索浏览器自身的DNS缓存,如果存在,则域名解析到此完成。
2、如果浏览器自身的缓存里面没有找到对应的条目,那么会尝试读取操作系统的hosts文件看是否存在对应的映射关系,如果存在,则域名解析到此完成。
3、如果本地hosts文件不存在映射关系,则查找本地DNS服务器(ISP服务器,或者自己手动设置的DNS服务器),如果存在,域名到此解析完成。
4、如果本地DNS服务器还没找到的话,它就会向根服务器发出请求,进行递归查询。
ii.CDN缓存
全称 Content Delivery Network,即内容分发网络。相当于代理服务器,将资源缓存到离用户最近的服务器上加快请求速度,从而降低网络延迟。
CDN节点缓存机制在不同服务商中是不同的,但一般都遵循HTTP协议,通过http响应头中的Cache-Control:max-age的字段来设置CDN节点文件缓存时间。
若使用了CDN缓存,再次请求时若浏览器本地缓存失效后,浏览器会向离用户最近的服务器边缘节点CDN发起请求。若本地缓存过期,览器不是直接向源站点请求资源,而是向CDN边缘节点请求资源,若CDN中的缓存也过期,那就由CDN边缘节点向源站点发出回源请求来获取最新资源。CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。
CDN优势
CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。
大部分请求在CND节点完成,CND起到分流作用,减轻源服务器负载。
三、浏览器同源策略和跨域
同源策略/SOP是一种约定。是指协议、域名、端口(默认80)三者相同。他是浏览器最核心也是最进本的安全功能,现在所有支持 JavaScript 的浏览器都会使用这个策略。如果缺少了同源策略,浏览器很容易受到 XSS、 CSFR 等攻击。
跨域:当协议、域名、端口有一个不同则跨域
四、浏览器端的渲染
浏览器内核指支持浏览器运行的最核心程序,分别为渲染引擎和JS引擎。在不同浏览器中渲染引擎是不同的,比如在 Firefox 中叫做 Gecko,在 Chrome 和 Safari 中都是基于 WebKit 开发的。
浏览器会解析三个东西,分别是html、css、js。对HTML和CSS构建时,如果遇到JS,会阻塞HTML和CSS的构建,优先加载JS文件,加载完毕,再从未加载的HTML和CSS开始继续构建。
浏览器会遵守一套步骤将HTML 文件转换为 DOM 树。宏观上,可以分为几个步骤
字节数据 => 字符串 => Token => Node => DOM
1、HTML文档解析并构建成DOM树
2、CSS解析并构建成CSS规则树
3、渲染树(Rendering Tree)是由DOM Tree 和 CSS Rule Tree的合并;渲染树并不是必须等DOM树及CSSOM树加载完成后才开始合并构建渲染树。三者的构建并无先后条件,不是完全独立,而是会有交叉,并行构建。因此会形成一边加载,一边解析,一边渲染的工作现象。
4、布局(layout),对渲染树的根节点开始遍历,计算渲染树的每个节
5、绘制(painting),将渲染树的各个节点绘制到屏幕上
回流(reflow)和重绘(repaint)
重绘: 渲染树节点外观、风格发生改变,但不影响该节点在页面当中的空间位置及大小。如某个div节点的背景色、字体色等发生改变,但是该div节点的宽、高、内外边距没变化,则触发浏览器重绘,即屏幕的一部分要重画。
回流:当渲染树节点发生改变,影响了节点的几何属性如宽、高、内边距、外边距、或是float、position、display:none;等,之后的所有节点位置都会发生改变,导致节点位置发生变化,此时触发浏览器重排(reflow),需要重新生成渲染树。此时浏览器需要重新生成渲染树,重新布局,即回流。
回流必定触发重绘,重绘不一定触发回流。
渲染阻塞
即执行JS脚本时,会导致DOM和CSSOM构建暂停,所以script标签位置很重要。JS阻塞了构建DOM树,也阻塞了其后的构建CSSOM规则树,整个解析进程必须等待JS的执行完成才能够继续,这就是所谓的JS阻塞页面。CSS阻塞渲染意味着,在CSSOM解析完前,页面将一直处理白屏状态,这就是为什么样式放在head中,仅仅是为了更快的解析CSS,保证更快的首次渲染。
默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到script标签就会暂停,等到执行完脚本,才继续渲染。如果是外部脚本,还必须加入脚本下载的时间。下载和执行的时间就会很长,因此造成浏览器阻塞,用户会感觉到浏览器“卡死”了。
script标签使用defer或async属性,脚本就会异步加载。defer,加载完后等整个页面都解析完毕后才开始执行。async,加载完后就立即执行。两者的区别在于脚本加载完成之后何时执行。
五、进程与线程
进程与线程概念
从本质上说,进程和线程都是 CPU 工作时间片的一个描述:
进程描述了CPU在运行指令和保存上下文所需的时间,放在应用上来说就代表了一个程序。
线程是进程中最小代为,藐视了执行一段指令所需的时间
进程是资源分配的最小单位;线程是CPU调度的最小单位
一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。进程是运行在虚拟内存上的,虚拟内存是用来解决用户对硬件资源的无限需求和有限的硬件资源之间的矛盾的。从操作系统角度来看,虚拟内存即交换文件;从处理器角度看,虚拟内存即虚拟地址空间。
如果程序很多时,内存可能会不够,操作系统为每个进程提供一套独立的虚拟地址空间,从而使得同一块物理内存在不同的进程中可以对应到不同或相同的虚拟地址,变相的增加了程序可以使用的内存。
进程和线程之间的关系特点
进程中的任意一线程执行出错,都会导致整个进程的崩溃。
线程之间共享数据
当一个进程关闭之后,操作系统会回收进程所占用的内存,**当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。
进程之间的内容相互隔离。**进程隔离就是为了使操作系统中的进程互不干扰,每一个进程只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况。正是因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,这时候,就需要使用用于进程间通信的机制了。
Chrome浏览器包括
1个浏览器主线程:主要负责页面显示、用户交互、子进程管理,同时提供存储等功能
1个GPU进程:GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
1个网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
多个渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
多个插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。
打开一个网页,最少需要四个进程:1个网络进程、一个浏览器进程、一个GPU进程以及一个渲染进程。如果打开的页面包括插件的话,还需要加上一个插件进程。
虽然多进程模型提升了浏览器的稳定性、流畅性和安全性,但同样不可笔墨安的带来了一些问题:
更高的资源占用:因为每个进程都会包含公共基础结构的副本(如 JavaScript 运行环境),这就意味着浏览器会消耗更多的内存资源。
更复杂的体系架构:浏览器各模块之间耦合性高、扩展性差等问题,会导致现在的架构已经很难适应新的需求了。
渲染进程(内核)
浏览器的渲染进程(内核)是核心重点,其中线程总共有五种
1.GUi渲染
负责渲染浏览器页面,解析HTML、CSS,构建DOM树、构建CSSOM树、构建渲染树和绘制页面;当界面需要重绘或由于某种操作引发回流时,该线程就会执行。
注意:GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
2.JS引擎
JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行代码;JS引擎线程一直等待着任务队列中任务的到来,然后加以处理,一个Tab页中无论什么时候都只有一个JS引擎线程在运行JS程序;
注意:GUI渲染线程与JS引擎线程的互斥关系,所以如果JS执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。
3.时间触发线程
事件触发线程
属于浏览器而不是JS引擎,用来控制事件循环;当JS引擎执行代码块如setTimeOut时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件触发线程中;当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;
注意:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行);
4..定时器触发线程
定时器触发线程即setInterval与setTimeout所在线程;浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;
注意:W3C在HTML标准中规定,定时器的定时时间不能小于4ms,如果是小于4ms,则默认为4ms。
5.异步HTTP请求线程
- XMLHttpRequest连接后通过浏览器新开一个线程请求;
- 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行
进程之前的通信方式
消息队列通信;管道通信;信号通信;共享内存通信等
浏览器内多标签通信
实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。通信方法如下:
使用 websocket 协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
使用 postMessage 方法,如果我们能够获得对应标签页的引用,就可以使用postMessage 方法,进行通信。
使用 localStorage 的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
使用 ShareWorker 的方式,shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个线程,然后通过这个共享的线程来实现数据的交换。
对Service Worker的理解
Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。
Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
六、浏览器内核
不同浏览器的内核也不尽相同,内核可区分为渲染引擎和JS引擎,所以不同浏览器对代码的解析会存在差异,这就导致对页面渲染效果不统一的问题。
市场上常见的浏览器内核主要有四种:Webkit内核、Presto内核、Trident内核、Gecko内核。
浏览器 | 内核 |
---|---|
Chorme | Webkit,现在是 Blink(Webkit的分支内核) |
Safari | Webkit |
Firefox | Gecko |
Opera | 最初是自己的Presto,后来是Webkit,现在是Blink |
IE | Trident(俗称IE内核) |
360、猎豹浏览器 | IE + Chorme双内核 |
搜狗、遨游、UC、QQ浏览器 | Trident(兼容模式)+ Webkit(高速模式) |
百度浏览器 | IE内核 |
七、浏览器的兼容性问题
HTML兼容
这是由于低版本浏览器不能识别一些高版本浏览器使用的标签,从而导致不能解析,比如HTML5新增的标签
CSS兼容
浏览器CSS样式兼容:
浏览器的默认样式不同,通常可以把一些常用的标签统一设置下。
浏览器私有属性前缀样式兼容:
-moz-代表firefox
-ms-代表IE
-webkit-代表chrome、safari
-o-代表opera
CSS hack语法兼容:
大致可以分为 条件hack、属性级hack、选择符级hack。
js兼容
event事件问题。DOM节点相关的问题。阻止事件问题。鼠标滚轮滚动事件问题等
八、浏览器相关状态码
1XX:消息
消息 | 描述 |
---|---|
100 Continue | 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。 |
101 Switching Protocols | 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。 |
2XX:成功
消息 | 描述 |
---|---|
200 OK | 请求成功(其后是对GET和POST请求的应答文档。) |
201 Created | 请求被创建完成,同时新的资源被创建。 |
202 Accepted | 供处理的请求已被接受,但是处理未完成。 |
203 Non-authoritative Information | 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。 |
204 No Content | 没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。 |
205 Reset Content | 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。 |
206 Partial Content | 客户发送了一个带有Range头的GET请求,服务器完成了它。 |
3XX:重定向
消息 | 描述 |
---|---|
300 Multiple Choices | 多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。 |
301 Moved Permanently | 所请求的页面已经转移至新的url。 |
302 Found | 所请求的页面已经临时转移至新的url。 |
303 See Other | 所请求的页面可在别的url下被找到。 |
304 Not Modified | 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。 |
305 Use Proxy | 客户请求的文档应该通过Location头所指明的代理服务器提取。 |
306 Unused | 此代码被用于前一版本。目前已不再使用,但是代码依然被保留。 |
307 Temporary Redirect | 被请求的页面已经临时移至新的url。 |
4XX:客户端错误
消息 | 描述 |
---|---|
400 Bad Request | 服务器未能理解请求。 |
401 Unauthorized | 被请求的页面需要用户名和密码。 |
402 Payment Required | 此代码尚无法使用。 |
403 Forbidden | 对被请求页面的访问被禁止。 |
404 Not Found | 服务器无法找到被请求的页面。 |
405 Method Not Allowed | 请求中指定的方法不被允许。 |
406 Not Acceptable | 服务器生成的响应无法被客户端所接受。 |
407 Proxy Authentication Required | 用户必须首先使用代理服务器进行验证,这样请求才会被处理。 |
408 Request Timeout | 请求超出了服务器的等待时间。 |
409 Conflict | 由于冲突,请求无法被完成。 |
410 Gone | 被请求的页面不可用。 |
411 Length Required | "Content-Length" 未被定义。如果无此内容,服务器不会接受请求。 |
412 Precondition Failed | 请求中的前提条件被服务器评估为失败。 |
413 Request Entity Too Large | 由于所请求的实体的太大,服务器不会接受请求。 |
414 Request-url Too Long | 由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况。 |
415 Unsupported Media Type | 由于媒介类型不被支持,服务器不会接受请求。 |
416 | 服务器不能满足客户在请求中指定的Range头。 |
417 Expectation Failed |
5XX服务器端错误
消息 | 描述 |
---|---|
500 Internal Server Error | 请求未完成。服务器遇到不可预知的情况。 |
501 Not Implemented | 请求未完成。服务器不支持所请求的功能。 |
502 Bad Gateway | 请求未完成。服务器从上游服务器收到一个无效的响应。 |
503 Service Unavailable | 请求未完成。服务器临时过载或当机。 |
504 Gateway Timeout | 网关超时。 |
505 HTTP Version Not Supported | 服务器不支持请求中指明的HTTP协议版本。 |