目录
1、跨域问题
跨域是由于浏览器的同源策略造成的,是浏览器施加的安全策略
同源策略(SOP):拥有相同的协议、域名、端口号的网址间才可以相互访问资源
跨域并不是请求发不出去,而是请求发出去了,也正常返回结果了,但结果被浏览器拦截了
常用的解决方案:JSONP、CROS、代理服务器
1)JSONP只接受GET请求
带有src属性的标签不受浏览器同源策略的影响,可以通过src属性请求非同源js脚本
2)CORS 设置响应头
response.setHeader('Access-Control-Allow-Origin','*')
默认情况在跨域请求,浏览器是不携带cookie的,但可以通过设置withCredentials来进行传递cookie');
//原生xml的设置方式
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
//axios设置方式
axios.defaults.withCredentials = true;
CORS和JSONP对比
CORS与JSONP相比,无疑更为先进、方便和可靠。
(1)JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求;
(2)使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理;
(3)JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS;
3)HTML5的window.postMessage方法跨域
为了解决跨域问题,HTML5引入一个全新的API:跨文档通信API(Cross-document messaging);这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
如:父窗口http://a.com向子窗口http://b.com发消息。
a页面
<iframe id="frame1" src="http://127.0.0.1/JSONP/b.html" frameborder="1"></iframe>
document.getElementById('frame1').onload = function(){
var win = document.getElementById('frame1').contentWindow;
win.postMessage("我是来自a页面的","http://127.0.0.1/JSONP/b.html")
}
b页面
window.onmessage = function(e){
e = e || event;
console.log(e.data);//我是来自a页面的
}
2、浏览器从输入URL到渲染页面的过程
1)URL解析:判断用户输入的是否是合法的URL(统一资源定位);如果不是则搜索引擎搜索,如果符合url规则则会根据url协议合成合法的url;
2)查找缓存:网络进程获取到url,先去本地缓存中查找是否有缓存资源,如果有则拦截请求,直接将缓存资源返回给浏览器,否则进入网络请求阶段;
3)DNS域名解析:DNS协议通过域名查找IP地址或是逆向地从IP地址查找域名的服务。DNS是一个网络服务器,上面存放了各个域名和其IP地址之间的关系数据;
DNS查找数据缓存服务中是否缓存过当前域名信息,有则直接返回,否则进行DNS解析返回域名对应的IP和端口号,没有指定端口号时,http默认为80端口,https默认为443端口;如果是https请求,还需要建立TLS连接;
4)TCP连接:
TCP三次握手与服务器建立连接,然后进行数据的传输;
发送HTTP请求,包含请求行、请求头、请求体;
服务器处理请求,返回响应行、响应头、响应体;
5)页面渲染:
①解析HTML的所有标签,深度遍历生成DOM树;
②解析CSS,构建层叠样式表模型(CSSOM);
②.₅JS脚本加载
a.普通JS/sync:文档解析过程中,如果遇到script脚本,就会停止页面的解析进行下载,当脚本都执行完毕后,才会继续解析页面;如果脚本是外部的会等待脚本下载完毕再继续解析文档;(因为JS脚本中可能会改动DOM和CSS继续解析会造成浪费)
b.async:async脚本会在加载完毕之后执行;async脚本的加载不计入DOMContentLoaded事件统计
第一种:HTML还没有被解析完时,async脚本已经加载完毕,则HTML停止解析,执行脚本,脚本执行完毕后触发DOMContentLoaded事件
第二种:HTML解析完后,async脚本才加载完毕,HTML解析完毕就触发DOMContentLoaded事件
c.defer:文档解析时遇到设置defer的脚本,就会再后台进行下载,但并不会阻止文档的渲染,当页面解析和渲染完毕后,等所有defer脚本加载并执行完毕后才会触发DOMContentLoaded事件
第一种:HTML还未解析完时,defer脚本加载完毕,等待HTML解析完后再执行脚本,脚本执行完毕后触发DOMContenLoaded事件
第二种:HTML解析完毕时,defer脚本还未加载完毕,等待defer加载和执行完毕后才触发DOMContentLoaded事件
③构建渲染树(Render Tree)DOM树和CSSOM根据一定的规则组合起来生成了渲染树;
④布局:根据渲染树确定各个元素的位置及大小;浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有元素;
⑤绘制:浏览器会遍历Render Tree,调用paint方法,将渲染树的各个节点绘制到屏幕上
重绘:外观发生改变,但没有改变布局;
因元素的颜色、字体等不改变尺寸和位置的样式改变而重新绘制,性能消耗较小
重排(回流):元素改变尺寸、宽高、边框、内容、位置等,导致需要重新构建页面
页面第一次渲染;增删可见DOM元素;元素尺寸、内容发生改变;
6)断开TCP连接:数据传输完成,正常情况下TCP四次挥手断开连接
3、session、cookie、sessionStorage、localStorage
cookie和session都是参与服务器通信的,而localStorage和sessionStorage不参与服务器通信;
3.1cookie和session
cookie和session是常用的会话跟踪技术;cookie通过在客户端记录信息确定用户身份,session通过服务器端记录信息确定用户身份;session较cookie更安全;
cookie的作用是在客户端保持状态,比如登陆状态。在发送http请求时会被放进请求头中参与通信。每个cookie最大为4k,每个域名可以拥有的cookie数量在不同浏览器中是不同的,但都多于20个,早期的20个限制已经不存在;
session的作用是在服务器端保持状态。session被创建的时候会生成一个sessionid,它被存储在cookie中用来访问session;由于关闭浏览器不会导致session被删,迫使服务器为session设置了失效时间;
3.2session和sessionStorage
session存储于服务器(后端);sessionStorage存储于客户端;
session对于服务器(后端)来说是一个标识;sessionStorage堆客户端(前端)来说是存储的功能
session存储维持会话状态的key,sessionStorage存储会话期间的数据;(浏览器的window对象中的sessionStorage中存贮着后台的session)
3.3localStorage和sessionStorage
浏览器本地存储分为:cookie,webStorage和IndexDB
localStorage和sessionStorage都属于webStorage本地存储,不参与服务器通信,都属于window对象,最大存储都在5M左右
localStorage是永久存储,可以用来长期保存登录信息 localStorage.getItem('TOKEN')
sessionStorage的存储时间是当前会话,关闭页面或浏览器就会被清除,可以用来一次性保存登录信息,而且不同浏览器不共享;
4、强缓存和协商缓存
浏览器缓存主要分为强缓存和协商缓存
Etag:客户端会通过If-None-Match将先前服务器端返回的Etag发送给服务器,服务器会对比这个客户端发送过来的Etag是否与服务器的相同,若相同,就将If-None-Match的值设为false,返回状态304,客户端继续使用本地缓存,不解析服务器端发过来的数据;若不相同就将If-None-Match的值设为true,返回状态为200,客户端重新解析服务器端返回来的数据;
If-Modified-Since:客户端还会通过If-Modified-Since头将先前服务器端发过来的最后修改时间戳发送给服务器,服务器通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回最新内容,如果是最新的,则返回304,客户端继续使用本地缓存;
强缓存:利用http头中的Expires和Cache-Control两个字段来控制,用来表示资源的缓存时间;
Expires:http1.0的规范,值为绝对时间的GMT格式的时间字符串
Expires:Mar, 06 Apr 2020 10:47:02 GMT
这个时间代表这个资源的失效时间,只要发送请求是在Expires之前,那么本地缓存始终有效,在缓存中读取数据;
缺点:由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱;
Cache-Control:http1.1中出现,一般利用该字段的max-age来判断,是一个相对时间
Cache-Control:max-age=3600 //代表资源有效期为3600秒
该字段的其他常用值:
- no-cache:不使用本地缓存;需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存在Etag,那么请求时就会与服务器端验证,如果验证通过且资源未被修改,则可以避免重新下载;
- no-store:直接禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源;
- public:可以被所用的用户缓存,包括中间用户和CDN等中间代理服务器;
- private:只能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器对其缓存;
Cache-Control和Expires同时配置时,Cache-Control的优先级更高;
强缓存的特点:请求时无需访问服务器,加载速度快,性能好,命中时直接返回成功200
未命中强缓存的情况:
- 响应头中没有Cache-Control和Expires
- Cache-Control和Expires过期
- Cache-Control属性设置为no-cache
协商缓存:未命中强缓存时,需访问服务器确认当前浏览器的缓存是否过期,若未过期则直接读取,返回304
主要涉及到两组header字段:Etag和If-None-Match;Last-Modify和If-Modified-Since
Last-Mody/If-Modified-Since
浏览器第一次请求一个资源时,服务器返回的header中会加上Last-Modify,Last-Modify是一个事件表示该资源的最后修改时间;当浏览器再次请求资源时,request的请求头中会包含If-Modified-Since,该值为缓存之前返回的Last-Modify;服务器收到If-Modified-Since后,根据资源的最后修改时间判断是否命中缓存,如果命中则返回304,并且不会返回资源内容和Last-Modify;
Etag和If-None-Match
Etag(Entity Tag)实体标签,是资源的一个唯一标识,资源变化都会导致Etag变化;服务器根据浏览器上送的If-None-Match值来判断是否命中缓存;
Etag的出现主要是为了解决:
- 一些文件也许会有周期性的更改,但内容不发生改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,If-Modified-Since能检查到的粒度是s级的,这种修改无法判断;
- 某些服务器不能精确的得到文件的最后修改时间
Last-Modify和Etag可以同时使用,服务器会优先验证Etag,一致情况下才会继续比较Last-Modify,最后才决定是否返回304;