https://blog.poetries.top/browser-working-principle/大佬写的很好,知识的搬运工是我。
1.同源策略及相关
- 源? 源(origin)就是协议、域名和端口号
url中的源:http://www.aspxfans.com:8080/news/index.asp?ID=24618&page=1#name- 协议:http://
- 服务器名:www
- 域名:aspxfans.com
- 端口:80
- 网站名:www.aspxfans.com
- 虚拟目录部分:/news/ (从域名后的第一个“/”开始到最后一个“/”为止,虚拟目录也不是一个URL必须的部分)
- 文件名部分:index.asp (从域名后的最后一个“/”开始到“?”为止,是文件名部分,如果没有“?”,则是从域名后的最后一个“/”开始到“#”为止,是文件部分,如果没有“?”和“#”,那么从域名后的最后一个“/”开始到结束,都是文件名部分。文件名部分也不是一个URL必须的部分,如果省略该部分,则使用默认的文件名)
- 参数部分:ID=24618&page=1(从“?”开始到“#”为止之间的部分为参数部分,又称搜索部分、查询部分。参数与参数之间用“&”作为分隔符)
- 锚部分:name(从“#”开始到最后,都是锚部分。锚部分也不是一个URL必须的部分)
- 同源策略
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。 - 同源检测
对http://www.a.com:80/test/index.html 的同源检测- http://www.a.com:80/dir/page.html ✅
- http://www.child.a.com:80/test/index.html ❌ 域名不同
- https://www.a.com:80/test/index.html ❌ 协议不同
- http://www.a.com:90/test/index.html ❌ 端口号不同
- 不受同源策略限制的有:
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的
- 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的< script src="…"></ script>,< img>,< link>,< iframe>等
2.跨域
不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域。
跨域的实现形式:
-
降域 document.domain
同源策略认为域和子域属于不同的域,如:- child1.a.com 与 a.com
- child1.a.com 与 child2.a.com
- xxx.child1.a.com 与 child1.a.com
两两不同源,可以通过设置 document.domain=‘a.com’,浏览器就会认为它们都是同一个源。想要实现以上任意两个页面之间的通信,两个页面必须都设置document.domain=‘a.com’。此方式的特点:
- 只能在父域名与子域名之间使用,且将 xxx.child1.a.com域名设置为a.com后,不能再设置成child1.a.com
- 存在安全性问题,当一个站点被攻击后,另一个站点会引起安全漏洞
- 这种方法只适用于 Cookie 和 iframe 窗口
-
JSONP跨域(JSONP和JSON并没有什么关系)
JSONP跨域原理:
举例:a.com/jsonp.html想得到b.com/main.js中的数据在a.com的jsonp.html里创建一个回调函数xxx,动态添加< script>元素,向服务器发送请求,请求地址后面加上查询字符串,通过callback参数指定回调函数的名字。请求地址为http://b.com/main.js?callback=xxx。在main.js中调用这个回调函数xxx,并且以JSON数据形式作为参数传递,完成回调。这样便实现了跨域的参数传递👇
- a.com/jsonp.html
function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag('http://b.com/main.js?callback=foo'); } //window.onload是为了让页面加载完成后再执行 function foo(data) { console.log(data.name+"欢迎您"); };
- b.com/main.js
foo({name:"hl"})
此方法的问题:
- 使用这种方法,只要是个网站都可以拿到b.com里的数据,存在安全性问题。需要网站双方商议基础token的身份验证
- 只能是GET,不能POST
- 可能被注入恶意代码,篡改页面内容,可以采用字符串过滤来规避此问题
- a.com/jsonp.html
-
CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。刚才的例子中,在b.com里面添加响应头声明允许a.com的访问,代码:
Access-Control-Allow-Origin: http://a.com
然后a.com就可以用ajax获取b.com里的数据了。
注意:此方法IE8以下完全不支持,IE8-10部分支持。详见caniuse-CORS -
其他方法
- HTML5的postMessage方法
- window.name
- location.hash
3.WebStorage和Cookie
web应用需要保存的是用户的会话状态、个性化信息。但是因为http协议是无状态的,因此这种需求是用Cookies来实现的。Cookies的特点是由服务端生成,通过响应发送到浏览器,浏览器会把cookies存储在本地,接下来的每次请求,都会自动带上Cookies到服务器。后来H5提供了新的本地存储API:WebStorage。WebStorage可以理解成Cookies的增强版。
两者区别:
cookie | webstorage |
---|---|
大小是受限的,并且每请求一次新的页面的时候,cookie都会被发送过去,这个无形中浪费了带宽 。需要指定作用域,是不可以跨域调用 | 是为了更大的容量存储设计的。比cookie大得多,可以达到5M或更大 |
cookie需要前端开发者自己封装setCookie,getCookie | web storage拥有setItem,getItem,removeItem,clear等方法 |
cookie也是不可或缺的,cookie的作用是与服务器进行交互,作为http规范的一部分而存在的 | web Storage仅仅是为了在本地“存储”数据而生。只能由客户端脚本访问,不能在服务端生成 |
1.WebStorage
WebStorage的作用是在于存储客户端数据在浏览器里,有两种机制:
- localStorage:同样的功能,但是在浏览器关闭然后重新打开后数据仍然可以存在。
- sessionStorage:为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面的重新加载和回复)。
这两种机制是通过Window.sessionStorage和Window.localStorage属性使用的(确切的说:在支持的浏览器中Window对象实现了WindowLocalStorage和WindowSessionStorage对象并挂在其localStorage和sessionStorage属性下)调用其中任一对象会创建Storage对象,通过Storage对象,可以设置、获取和移除数据项。对于每个源(origin),sessionStorage和localStorage使用不同的Storage对象,独立运行和控制。
例如,在文档中调用 localStorage 将会返回一个 Storage 对象;调用 sessionStorage 返回一个不同的 Storage 对象。可以使用相同的方式操作这些对象,但是操作是独立的。
-
举例LocalStorage
if(!window.localStorage){ alert("浏览器不支持localstorage"); }else{ var storage=window.localStorage; storage["a"]=1; //写入a字段 storage.a=1; //写入b字段 storage.setItem("c",3); //写入c字段 var a=storage.a; //第一种方法读取 var b=storage["b"]; //第二种方法读取 var c=storage.getItem("c"); //第三种方法读取 storage.removeItem("a"); // 某个键值对删除 storage.clear(); // 所有内容清除 // 键的获取 for(var i=0;i<storage.length;i++){ var key=storage.key(i); console.log(key); } // localstorage 存取 json 格式 var data = { test:"text", test1:"123456", test2:"字段值", } localStorage.setItem("data", JSON.stringify(data)); // 存 var data = localStorage.getItem("data"); // 取 data = JSON.parse(data); }
-
通过StorageEvent响应存储的变化
无论何时,storage对象(localstorage/sessionstorage)发生变化时(即创建/更新/删除数据项时,重复设置相同的键值不会触发该事件,Storage.clear() 方法至多触发一次该事件),StorageEvent 事件会触发。当前页面使用的Storage被其他页面修改时会触发StorageEvent事件(事件在同一个域下的不同页面之间触发,即在A页面注册了storge的监听处理,只有在跟A同域名下的B页面操作storage对象,A页面才会被触发storage事件)。在其他域名下的页面不能访问相同的 Storage 对象。
4.进程和线程
-
并行处理
同一时刻处理多个任务。 -
线程
多线程可以并行处理任务,但是线程是不能单独存在的,它是由进程来启动和管理的。 -
进程
从图中可以看到,线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。
一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。进程和线程之间的关系有以下4个特点:
- 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
- 线程之间共享进程中的数据。
- 当一个进程关闭之后,操作系统会回收进程所占用的内存。
- 进程之间的内容相互隔离。
看一下早期的单进程浏览器的架构图:
早期的(2008)Chrome多进程浏览器的架构图:
注:IPC机制用于进程之间进行通信
近期的Chrome多进程浏览器的架构图:
未来面向服务的浏览器架构:
Chrome 整体架构会朝向现代操作系统所采用的“面向服务的架构” 方向发展,原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过IPC来通信,从而构建一个更内聚、松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。
Chrome最终要把UI、数据库、文件、设备、网络等模块重构为基础服务,类似操作系统底层服务,下面是Chrome“面向服务的架构”的进程模型图:
5.TCP协议:如何保证页面文件能被完整送达浏览器
理解网络的关键是要对网络协议有深刻的认识,不管你是使用HTTP,还是使用WebSocket,它们都是基于TCP/IP的,如果你对这些原理有足够了解,也就清楚如何去优化Web性能,或者能更轻松地定位Web问题了。
互联网,实际上是一套理念和协议组成的体系架构。其中,协议是一套众所周知的规则和标准,如果各方都同意使用,那么它们之间的通信将变得毫无障碍
在网络中,一个文件通常会被拆分为很多数据包来进行传输,而数据包在传输过程中又有很大概率丢失或者出错。那么如何保证页面文件能被完整地送达浏览器呢?
-
IP:把数据包送达目的主机
数据包要在互联网上进行传输,就要符合网际协议(Internet Protocol,简称IP)标准。互联网上不同的在线设备都有唯一的地址,地址只是一个数字。计算机的地址就称为IP地址,访问任何网站实际上只是你的计算机向另外一台计算机请求信息。为了方便理解,先把网络简单分为三层结构👇
- 上层将含有“极客时间”的数据包交给网络层;
- 网络层再将IP头附加到数据包上,组成新的 IP数据包,并交给底层;
- 底层通过物理网络将数据包传输给主机B;
- 数据包被传输到主机B的网络层,在这里主机B拆开数据包的IP头信息,并将拆开来的数据部分交给上层;
- 最终,含有“极客时间”信息的数据包就到达了主机B的上层了
-
UDP:把数据包送达应用程序
IP是非常底层的协议,只负责把数据包传送到对方电脑,但是对方电脑并不知道把数据包交给哪个程序,是交给浏览器还是交给王者荣耀?因此,需要基于IP之上开发能和应用打交道的协议,最常见的是“用户数据包协议(User Datagram Protocol)”,简称UDP。UDP中一个最重要的信息是端口号。所以IP通过
IP地址
信息把数据包发送给指定的电脑,而UDP通过端口号
把数据包分发给正确的程序。- 上层将含有“极客时间”的数据包交给传输层;
- 传输层会在数据包前面附加上UDP头,组成新的UDP数据包,再将新的UDP数据包交给网络层;
- 网络层再将IP头附加到数据包上,组成新的IP数据包,并交给底层;
- 数据包被传输到主机B的网络层,在这里主机B拆开IP头信息,并将拆开来的数据部分交给传输层;
- 在传输层,数据包中的UDP头会被拆开,并根据UDP中所提供的端口号,把数据部分交给上层的应用程序;
- 最终,含有“极客时间”信息的数据包就旅行到了主机B上层应用程序这里。
在使用UDP发送数据时,有各种因素会导致数据包出错,虽然UDP可以校验数据是否正确,但是对于错误的数据包,UDP并不提供重发机制,只是丢弃当前的包,而且UDP在发送之后也无法知道是否能达到目的地。
虽说UDP不能保证数据可靠性,但是传输速度却非常快,所以UDP会应用在一些关注速度、但不那么严格要求数据完整性的领域,如在线视频、互动游戏等。
-
TCP:把数据完整地送达应用程序
对于浏览器请求,或者邮件等这类要求数据传输可靠性(reliability)的应用,引入了TCP。TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。相对于UDP,TCP有下面两个特点:- 对于数据包丢失的情况,TCP提供重传机制;
- TCP引入了数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件。
一个完整的TCP连接的生命周期包括了“建立连接”“传输数据”和“断开连接”三个阶段:
- 建立连接阶段
这个阶段是通过“三次握手”来建立客户端和服务器之间的连接。TCP 提供面向连接的通信传输。面向连接是指在数据通信开始之前先做好两端之间的准备工作。所谓三次握手,是指在建立一个TCP连接时,客户端和服务器总共要发送三个数据包以确认连接的建立。 - 传输数据阶段
在该阶段,接收端需要对每个数据包进行确认操作,也就是接收端在接收到数据包之后,需要发送确认数据包给发送端。所以当发送端发送了一个数据包之后,在规定时间内没有接收到接收端反馈的确认消息,则判断为数据包丢失,并触发发送端的重发机制。同样,一个大的文件在传输过程中会被拆分成很多小的数据包,这些数据包到达接收端后,接收端会按照TCP头中的序号为其排序,从而保证组成完整的数据。 - 断开连接阶段
数据传输完毕之后,就要终止连接了,涉及到最后一个阶段“四次挥手”来保证双方都能断开连接。
TCP为了保证数据传输的可靠性,牺牲了数据包的传输速度,因为“三次握手”和“数据包校验机制”等把传输过程中的数据包的数量提高了一倍。