一、浏览器
什么是URL
组成部分: protocol:协议,表明浏览器必须使用何种协议 domain:域名,表示正在请求哪个服务器 port:端口 path:网络服务器上资源的路径 parameters/query:提供给网络服务器的额外参数 anchor/fragment:资源本身的另一部分的锚点,锚点表示资源中的一种“书签”,给浏览器显示位于该“加书签”位置的内容的方向 scheme:// user:passwd@ host:port path ?query #fragment scheme 表示协议名,比如http, https, file等等。后面必须和://连在一起。 user:passwd@ 表示登录主机时的用户信息,不过很不安全,不推荐使用,也不常用。 host:port表示主机名和端口。 path表示请求路径,标记资源所在位置。 query表示查询参数,为key=val这种形式,多个键值对之间用&隔开。 fragment表示 URI 所定位的资源内的一个锚点,浏览器可以根据这个锚点跳转到对应的位置。 https://www.baidu.com/s?wd=HTTP&rsv_spt=1 这个 URI 中,https即scheme部分,www.baidu.com为host:port部分,域名部分:端口号(注意,http 和 https 的默认端口分别为80、443),/s为path部分,而wd=HTTP&rsv_spt=1就是query部分。
什么是BOM,有哪些常用的BOM 对象及其属性?
location对象:用于获取或设置窗体的URL,并且可以用于解析URL 属性:hash\host\hostname… 其他方法: location.search 返回参数 location.assign() 跳转页面,可以重定向页面 location.replace() 替换当前页面,但是没有历史记录,不可回退 location.reload() history对象:记录用户曾经浏览过的页面(URL),并可以实现浏览器的前进与后退相似导航功能 属性:length 返回浏览器历史列表中的URL数量 方法: back() 加载history列表中的前一个URL forward() 加载history列表中的下一个URL go() 加载history列表中的某个具体的页面 navigator对象:包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本 appCodeName 浏览器代码名的字符串表示 appName 返回浏览器的名称 appVersion 返回浏览器的平台和版本信息 platform 返回运行浏览器的操作系统平台 userAgent 返回由客户端发送服务器的user-agent头部的值
CSRF和XSS攻击及防御手段
CSRF(跨站请求伪造) 方式: 攻击者在页面中嵌入恶意JS脚本,当用户浏览该页面时进行攻击 防御:cookie设置httponly和secure;进行特殊字符过滤;对用户的输入进行检查; 如何防范XSS: 在http头部配上set-cookie,其中 httponly:该属性会禁止JS脚本使用document.cookie来访问cookie secure:该属性告诉浏览器仅在请求为HTTPS的时候才发送cookie XSS(跨站脚本攻击) 方式:攻击者盗用用户身份,以用户名义发送恶意请求 防御:使用验证码;检查HTTPS头部referer字段 Referer 首部包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。组成:协议+域名+端口号+路径+参数
什么是中间人攻击?
此时?带来?个问题,MITM中间人攻击:攻击者相当于一个介入通信的传话员,攻击者知道通信双方的所有通信内容且可以任意增加、删除、修改双方的通信内容,而双方对此并不知情。 如果此时在客户端和服务器之间存在?个中间?,这个中间?只需要把原本双?通信互发的公钥,换成??的公钥,这样中间?就可以轻松解密通信双?所发送的所有数据。 所以这个时候需要?个安全的第三?颁发证书CA,证明身份的身份,防?被中间?攻击。 证书中包括:签发者、证书?途、使?者公钥、使?者私钥、使?者的HASH算法、证书到期时间等。 但是问题来了,如果中间?篡改了证书,那么身份证明是不是就?效了?这个证明就?买了,这个时候需要?个新的技术,数字签名。 数字签名就是?CA?带的HASH算法对证书的内容进?HASH得到?个摘要,再?CA的私钥加密,最终组成数字签名。当别?把他的证书发过来的时候,我再?同样的Hash算法,再次?成消息摘要,然后?CA的公钥对数字签名解密,得到CA创建的消息摘要,两者??,就知道中间有没有被?篡改了。这个时候就能最?程度保证通信的安全了。 6. 有哪些可能引起前端安全的问题? 7. 网络劫持有哪几种,如何防范?
二、进程与线程
进程与线程
什么是进程? CPU是计算机的核心,承担所有的计算任务,官网说法,进程是CPU资源分配的最小单位,字面意思就是进行中的程序,我将它理解为一个可以独立运行且拥有自己的资源空间的任务程序。进程包括运行中的程序和程序所使用到的内存和系统资源。 什么是线程? 线程是CPU调度的最小单位,线程是建立在进程的基础上的一次程序运行单位,通俗点解释线程就是程序中的一个执行流,一个进程可以有多个线程。一个进程中只有一个执行流称作单线程,即程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。一个进程中有多个执行流称作多线程,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
进程与线程的区别
(1)进程是操作系统分配资源的最小单位,线程是程序执行的最小单位; (2)一个进程由一个或多个线程组成,线程可以理解为是一个进程中代码的不同执行路线; (3)进程之间相互独立,但同一进程下的各个线程间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号); (4)调度和切换:线程上下文切换比进程上下文切换要快得多
多进程和多线程
多进程:多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。多进程带来的好处是明显的,比如大家可以在网易云听歌的同时打开编辑器敲代码,编辑器和网易云的进程之间不会相互干扰 多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务
JS为什么单线程的
JS的单线程,与它的用途有关。 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准。有人说,js还有Worker线程,对的,为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程是完 全受主线程控制的,而且不得操作DOM。所以,这个标准并没有改变JavaScript是单线程的本质。
进程之间的通信方式
无名管道:半双工通信方式,数据只能单向流动且只能在有亲缘关系的进程间使用 有名管道:半双工通信方式,允许在非亲缘关系的进程间使用 信号:通知接收进程某个事件已发生 消息队列:传递消息的链表,存放在内核中。克服了信号传输信息少,管道只能传输无格式字节流以及缓冲区大小受限的缺点 信号量:一个计数器,用来控制多个进程对共享资源的访问。常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源 共享内存:映射一份能被其他进程所访问的内存,这份内存由一个进程创建但其他进程可以访问 套接字:不同机器之间的进程通信
进程的三种状态
就绪(Ready)状态 进程已分配到除CPU以外的所有必要资源,只要获得处理机便可立即执行。 执行(Running)状态 进程已获得处理机,其程序正在处理机上执行。 阻塞(Blocked)状态 正在执行的程序,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的原因可能是等待I/O完成、申请缓冲区不能满足、等待信号等。 什么是僵尸进程? 僵尸进程是已完成且处于终止状态,但在进程表中却仍然存在的进程。僵尸进程通常发生在父子关系的进程中,由于父进程仍需要读取其子进程的退出状态所造成的。
死锁产生的原因? 如果解决死锁的问题
死锁: 是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 系统中的资源分为两类: 可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源; 不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。 产生死锁的原因: (1)竞争资源: (2)进程之间推进顺序非法:若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞 产生死锁的必要条件: 互斥条件:一个资源每次只能被一个进程使用 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放 不剥夺条件:进程已获得的资源在未使用完之前不能强行剥夺 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系 预防死锁的方法: 资源一次性分配:预先分配资源,即进程在运行前一次性地向系统申请它所需要的的全部资源。若某个进程所需的全部资源得不到满足则不分配任何资源,此进程暂不运行,直到系统能够满足当前进程的全部资源时才一次性将所申请的资源全部分配给该进程。(破坏请求条件) 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件) 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件) 7. 如何实现浏览器内多个标签页之间的通信???? 8. 对Service Worker的理解(缓存相关)????
浏览器包含哪些进程
(1)Browser进程: 浏览器的主进程,负责协调、主控,该进程只有一个; 负责浏览器界面显示,与用户进行交互。比如前进、后退; 负责各个页面的管理,创建和销毁其他进程; 将渲染进程得到的内存中的位图Bitmap,绘制到用户界面上; 网络资源的管理、下载; (2)第三方插件进程: 每个类型的插件对应一个进程、当使用该插件的时候才创建; (3)GPU进程: 该进程只有一个,用于3D绘制等; (4)渲染进程: 就是通常所说的浏览器内核Renderer进程,内部是多线程; 每个Tab页面有一个渲染进程,互不影响; 主要进行页面的渲染、脚本执行、事件处理等;
渲染进程的常用线程
GUI渲染进程: -负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等 解析html代码(HTML代码本质是字符串)转化为浏览器认识的节点,生成DOM树,也就是DOM Tree 解析css,生成CSSOM(CSS规则树) 把DOM Tree 和CSSOM结合,生成Rendering Tree(渲染树) -当我们修改了一些元素的颜色或者背景色,页面就会重绘(Repaint) -当我们修改元素的尺寸,页面就会重排(Reflow也叫回流) -当页面需要Repaing和Reflow时GUI线程执行,绘制页面 -回流(Reflow)比重绘(Repaint)的成本要高,我们要尽量避免Reflow和Repaint -GUI渲染线程与JS引擎线程是互斥的 当JS引擎执行时GUI线程会被挂起(相当于被冻结了) GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行 JS引擎线程 -JS引擎线程就是JS内核,负责处理Javascript脚本程序(例如V8引擎) -JS引擎线程负责解析Javascript脚本,运行代码 -JS引擎一直等待着任务队列中任务的到来,然后加以处理 浏览器同时只能有一个JS引擎线程在运行JS程序,所以js是单线程运行的 一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序 -GUI渲染线程与JS引擎线程是互斥的,js引擎线程会阻塞GUI渲染线程 就是我们常遇到的JS执行时间过长,造成页面的渲染不连贯,导致页面渲染加载阻塞(就是加载慢) 例如浏览器渲染的时候遇到
浏览器渲染流程
(1)解析 HTML 文件生成DOM 树 (2)解析 CSS 生成CSSOM规则树 (3)结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment (3)遍历渲染树开始布局,计算每个节点的位置大小信息; (4)将渲染树每个节点绘制到屏幕; (5)浏览器主进程将默认的图层和复合图层交给 GPU 进程,GPU 进程再将各个图层合成(composite),最后显示出页面
什么是渲染层合并 (Composite) ? 渲染层合并,对于页面中 DOM 元素的绘制(Paint)是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将绘制的位图发送给 GPU 绘制到屏幕上,将所有层按照合理的顺序合并成一个图层,然后在屏幕上呈现。
三、浏览器缓存
http缓存的作用?
节省资源,节省流量,节省时间,也就是所谓的优化。 时间: 通常情况下通过网络获取内容速度慢成本高,有些响应需要在客户端和服务器之间进行多次往返通信,这就拖延了浏览器可以使用和处理内容的时间,同时也增加了访问者的数据成本。 资源: 通过缓存,使用资源副本,大大减少获取资源时间, 流量: 能够减少网络带宽消耗、减少延迟与网络阻塞,同时降低服务器压力,提高服务器性能。
HTTP缓存方式
两种缓存方式,根据响应的header内容来决定 强缓存(状态码:200):浏览器不向服务器发送任何请求,直接从本地缓存中读取文件并返回(相关字段:Cache-Control、Expires) 协商缓存(状态码304):浏览器发送请求到服务器,通过服务器来告知缓存是否可用(相关字段:Last-Modified/If-Modified-Since、Etag/If-None-Match)
强缓存
强缓存:使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。 Expires:服务器通过在响应头中添加 Expires 属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。Expires 是 http1.0 中的方式,因为它的一些缺点,在 HTTP 1.1 中提出了一个新的头部属性就是 Cache-Control 属性。 Cache-Control: *max-age=xxx:缓存的内容将在 xxx 秒后失效,这个选项只在 HTTP1.1 可用,并如果和 Last-Modified 一起使用时,优先级较高。 *no-cache: 在浏览器使用缓存前,会往返对比 ETag,如果 ETag 没变,返回 304,则使用协商缓存。no-cache的目的就是为了防止从缓存中获取过期的资源。 *no-store: 彻底禁用缓存,所有内容都不会被缓存到缓存或临时文件中,每次都会向服务端发起新的请求,拉取最新的资源; *public: 所有内容都将被缓存(客户端和代理服务器都可缓存) *private: 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存)
协商缓存
它是一种服务端的缓存策略,即通过服务端来判断某件事情是不是可以被缓存。 使用协商缓存策略时,服务端判断客户端的资源,是否和服务端资源一样,如果一致则返回 304 ,反之返回 200 和最新的资源。 在响应头部 Response Headers 中,有两种资源标识: Last-Modified 资源的最后修改时间,对应请求头为 If-Modified-Since ; Etag 资源的唯一标识,所谓唯一,可以想象成时人类的指纹,具有唯一性;但 Etag 的本质是一个字符串;对应请求头为 If-None-Match Last-Modified 和 Etag 当响应头部 Response Headers 同时存在 Last-Modified 和 Etag 的值时,会优先使用 Etag ; Last-Modified 只能精确到秒级; 如果资源被重复生成,而内容不变,则 Etag 更精确 https://juejin.cn/post/6974529351270268958#heading-18
浏览器缓存的全过程
浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header,以供下次加载时对比使用;
下一次加载资源时,由于强制缓存优先级较高,先比较当前时间与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,直接从本地读取资源。如果浏览器不支持HTTP1.1,则使用 expires 头判断是否过期;
如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带有 If-None-Match 和 If-Modified-Since 的请求;
服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改,Etag 值一致则没有修改,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的 Etag 值并返回 200;
如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回 304;不一致则返回新的 last-modified 和文件并返回 200;
为什么需要浏览器缓存?
如果没有缓存的话,每一次网络请求都要加载大量的图片和资源,这会使页面的加载变慢许多。那缓存的目的其实就是为了尽量减少网络请求的体积和数量,让页面加载的更快。
使用浏览器缓存,有以下优点: 减少了服务器的负担,提高了网站的性能 加快了客户端网页的加载速度 减少了多余网络数据传输 哪些资源可以被缓存?——静态资源(js、css、img) 网站的 html 是不能被缓存的。因为网站在使用过程中 html 随时有可能被更新,随时有可能被替换模板。 网页的业务数据也是不能被缓存的。比如留言板和评论区,用户随时都可以在底下评论,那数据库的内容就会被频繁被更新
点击刷新按钮或者按 F5、按 Ctrl+F5 (强制刷新)、地址栏回车有什么区别?
点击刷新按钮或者按 F5:强制缓存失效,协商缓存有效 浏览器直接对本地的缓存文件过期,但是会带上If-Modifed-Since,If-None-Match,这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200 用户按 Ctrl+F5(强制刷新):强制缓存失效,协商缓存失效 浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200 地址栏回车:强制缓存有效,协商缓存有效 浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容。
四、浏览器组成
对浏览器的理解
浏览器的主要功能是将用户选择的 web 资源呈现出来,它需要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式通常是 HTML,也包括 PDF、image 及其他格式。用户用 URI(Uniform Resource Identifier 统一资源标识符)来指定所请求资源的位置。 浏览器可以分为两部分,shell 和 内核。其中 shell 的种类相对比较多,内核则比较少。也有一些浏览器并不区分外壳和内核。从 Mozilla 将 Gecko 独立出来后,才有了外壳和内核的明确划分。 shell 是指浏览器的外壳:例如菜单,工具栏等。主要是提供给用户界面操作,参数设置等等。它是调用内核来实现各种功能的。 内核是浏览器的核心,内核是基于标记语言显示内容的程序或模块。
对浏览器内核的理解
浏览器内核主要分成两部分: 渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。默认情况下,渲染引擎可以显示 html、xml 文档及图片,它也可以借助插件显示其他类型数据,例如使用 PDF 阅读器插件,可以显示 PDF 格式。 JS 引擎:解析和执行 javascript 来实现网页的动态效果。 最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。 3. 常见的浏览器内核比较
常见浏览器所用内核
(1) IE 浏览器内核:Trident 内核,也是俗称的 IE 内核; (2) Chrome 浏览器内核:统称为 Chromium 内核或 Chrome 内核,以前是 Webkit 内核,现在是 Blink内核; (3) Firefox 浏览器内核:Gecko 内核,俗称 Firefox 内核; (4) Safari 浏览器内核:Webkit 内核; (5) Opera 浏览器内核:最初是自己的 Presto 内核,后来加入谷歌大军,从 Webkit 又到了 Blink 内核; (6) 360浏览器、猎豹浏览器内核:IE + Chrome 双内核; (7) 搜狗、遨游、QQ 浏览器内核:Trident(兼容模式)+ Webkit(高速模式); (8) 百度浏览器、世界之窗内核:IE 内核;
浏览器的主要组成部分
?户界? - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗?显示的您请求的??外,其他显示的各个部分都属于?户界?。 浏览器引擎 - 在?户界?和呈现引擎之间传送指令。 呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。 ?络 - ?于?络调?,?如 HTTP 请求。其接?与平台?关,并为所有平台提供底层实现。 ?户界?后端 - ?于绘制基本的窗??部件,?如组合框和窗?。其公开了与平台?关的通?接?,?在底层使?操作系统的?户界??法。 JavaScript 解释器。?于解析和执? JavaScript 代码。 数据存储 - 这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“?络数据库”,这是?个完整(但是轻便)的浏览器内数据库。
五、浏览器渲染原理
浏览器包含哪些进程
(1)Browser进程: 浏览器的主进程,负责协调、主控,该进程只有一个; 负责浏览器界面显示,与用户进行交互。比如前进、后退; 负责各个页面的管理,创建和销毁其他进程; 将渲染进程得到的内存中的位图Bitmap,绘制到用户界面上; 网络资源的管理、下载; (2)第三方插件进程: 每个类型的插件对应一个进程、当使用该插件的时候才创建; (3)GPU进程: 该进程只有一个,用于3D绘制等; (4)渲染进程: 就是通常所说的浏览器内核Renderer进程,内部是多线程; 每个Tab页面有一个渲染进程,互不影响; 主要进行页面的渲染、脚本执行、事件处理等;
渲染进程的常用线程
GUI渲染进程: -负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等 解析html代码(HTML代码本质是字符串)转化为浏览器认识的节点,生成DOM树,也就是DOM Tree 解析css,生成CSSOM(CSS规则树) 把DOM Tree 和CSSOM结合,生成Rendering Tree(渲染树) -当我们修改了一些元素的颜色或者背景色,页面就会重绘(Repaint) -当我们修改元素的尺寸,页面就会重排(Reflow也叫回流) -当页面需要Repaing和Reflow时GUI线程执行,绘制页面 -回流(Reflow)比重绘(Repaint)的成本要高,我们要尽量避免Reflow和Repaint -GUI渲染线程与JS引擎线程是互斥的 当JS引擎执行时GUI线程会被挂起(相当于被冻结了) GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行 JS引擎线程 -JS引擎线程就是JS内核,负责处理Javascript脚本程序(例如V8引擎) -JS引擎线程负责解析Javascript脚本,运行代码 -JS引擎一直等待着任务队列中任务的到来,然后加以处理 浏览器同时只能有一个JS引擎线程在运行JS程序,所以js是单线程运行的 一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序 -GUI渲染线程与JS引擎线程是互斥的,js引擎线程会阻塞GUI渲染线程 就是我们常遇到的JS执行时间过长,造成页面的渲染不连贯,导致页面渲染加载阻塞(就是加载慢) 例如浏览器渲染的时候遇到
浏览器渲染流程
(1)解析 HTML 文件生成DOM 树 (2)解析 CSS 生成CSSOM规则树 (3)结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment (3)遍历渲染树开始布局,计算每个节点的位置大小信息; (4)将渲染树每个节点绘制到屏幕; (5)浏览器主进程将默认的图层和复合图层交给 GPU 进程,GPU 进程再将各个图层合成(composite),最后显示出页面 什么是渲染层合并 (Composite) ? 渲染层合并,对于页面中 DOM 元素的绘制(Paint)是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将绘制的位图发送给 GPU 绘制到屏幕上,将所有层按照合理的顺序合并成一个图层,然后在屏幕上呈现。
浏览器渲染优化方法…
(1)针对JS:JavaScript既会阻塞HTML的解析,也会阻塞CSS的解析。因此我们可以对JavaScript的加载方式进行改变,来进行优化 尽量将JavaScript文件放在body的最后; body中间尽量不要写
渲染过程中遇到 JS 文件如何处理?
JavaScript 的加载、解析与执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行完毕,浏览器再从中断的地方恢复继续解析文档。也就是说,如果想要首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。
什么是文档的预解析?
Webkit 和 Firefox 都做了这个优化,当执行 JavaScript 脚本时,另一个线程解析剩下的文档,并加载后面需要通过网络加载的资源。这种方式可以使资源并行加载从而使整体速度更快。需要注意的是,预解析并不改变 DOM 树,它将这个工作留给主解析过程,自己只解析外部资源的引用,比如外部脚本、样式表及图片。
CSS 如何阻塞文档解析?
理论上,既然样式表不改变 DOM 树,也就没有必要停下文档的解析等待它们。然而,存在一个问题,JavaScript 脚本执行时可能在文档的解析过程中请求样式信息,如果样式还没有加载和解析,脚本将得到错误的值,显然这将会导致很多问题。所以如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟 JavaScript 脚本执行和文档的解析,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后再执行 JavaScript,最后再继续文档的解析。
如何优化关键渲染路径CRP?
关键渲染路径是浏览器将 HTML CSS JavaScript 转换为在屏幕上呈现的像素内容所经历的一系列步骤。也就是我们上面说的浏览器渲染流程。 为尽快完成首次渲染,我们需要最大限度减小以下三种可变因素: 关键资源的数量: 可能阻止网页首次渲染的资源。 关键路径长度: 获取所有关键资源所需的往返次数或总时间。 关键字节: 实现网页首次渲染所需的总字节数,等同于所有关键资源传送文件大小的总和 优化常规步骤: 分析并用 关键资源数 关键字节数 关键路径长度 来描述我们的 CRP 最小化关键资源数: 消除它们(内联)、推迟它们的下载(defer)或者使它们异步解析(async)等等 。 优化关键字节数(缩小、压缩)来减少下载时间 。 优化加载剩余关键资源的顺序: 让关键资源(CSS)尽早下载以减少 CRP 长度
什么情况会阻塞渲染?
首先渲染的前提是生成渲染树,所以 HTML 和 CSS 肯定会阻塞渲染。如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。 然后当浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。
在地址栏里输入一个URL,到这个页面呈现出来,中间会发生什么?
简答: 根据域名,进行DNS域名解析; 拿到解析的IP地址,建立TCP连接; 向IP地址,发送HTTP请求; 服务器处理请求; 返回响应结果; 关闭TCP连接; 浏览器解析HTML; 浏览器布局渲染; 详细叙述: 1.根据URL域名寻找服务器ip,浏览器首先在缓存中查找,查找的顺序是浏览器DNS缓存→系统缓存→路由器缓存,缓存中查找不到则去本地域名服务器中查找,没有则继续向上查找 2.得到ip地址后,浏览器根据ip和相应端口号构建一个http请求并将该http请求封装在一个tcp包中,这个tcp包依次经过传输层、网络层、数据链路层、物理层到达服务器,服务器解析这个请求并作出响应,返回相应的html给浏览器,关闭TCP连接; 3.浏览器根据返回的html来构建DOM树,构建DOM树的过程中如果遇到图片、音视频等资源会并行下载,如果遇到js脚本或外部js连接,则会停止DOM树的构建去执行和下载相应js脚本,这会造成阻塞;之后根据外部样式、内部样式、内联样式构建CSSOM树,构建完成后和DOM树合并成渲染树,主要目的是排除非视觉节点,比如script、meta标签和排除display为none的节点; 4.进行布局,确定各个元素的位置和尺寸,然后渲染页面,显示给用户 5.上述所有请求中都会涉及http缓存机制
六、浏览器本地存储
浏览器本地存储方式及使用场景
(1)Cookie Cookie是最早被提出来的本地存储方式,在此之前,服务端是无法判断网络中的两个请求是否是同一用户发起的,为解决这个问题,Cookie就出现了。Cookie的大小只有4kb,它是一种纯文本文件,每次发起HTTP请求都会携带Cookie。 cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。 Cookie的特性: Cookie一旦创建成功,名称就无法修改 Cookie是无法跨域名的,也就是说a域名和b域名下的cookie是无法共享的,这也是由Cookie的隐私安全性决定的,这样就能够阻止非法获取其他网站的Cookie 每个域名下Cookie的数量不能超过20个,每个Cookie的大小不能超过4kb 有安全问题,如果Cookie被拦截了,那就可获得session的所有信息,即使加密也于事无补 Cookie在请求一个新的页面的时候都会被发送过去 使用场景: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能 (2)LocalStorage LocalStorage是HTML5新引入的特性,由于有的时候我们存储的信息较大,Cookie就不能满足我们的需求,这时候LocalStorage就派上用场了。 LocalStorage的优点: 在大小方面,LocalStorage的大小一般为5MB,可以储存更多的信息 LocalStorage是持久储存,并不会随着页面的关闭而消失,除非主动清理,不然会永久存在 仅储存在本地,不像Cookie那样每次HTTP请求都会被携带 LocalStorage的缺点: 存在浏览器兼容问题,IE8以下版本的浏览器不支持 如果浏览器设置为隐私模式,那我们将无法读取到LocalStorage LocalStorage受到同源策略的限制 LocalStorage的常用API: localStorage.setItem(‘key’, ‘value’); let data = localStorage.getItem(‘key’); localStorage.removeItem(‘key’); localStorage.clear(); localStorage.key(index) LocalStorage的使用场景: 有些网站有换肤的功能,这时候就可以将换肤的信息存储在本地的LocalStorage中,当需要换肤的时候,直接操作LocalStorage即可 在网站中的用户浏览信息也会存储在LocalStorage中,还有网站的一些不常变动的个人信息等也可以存储在本地的LocalStorage中 (3)SessionStorage SessionStorage和LocalStorage都是在HTML5才提出来的存储方案,SessionStorage 主要用于临时保存**同一窗口(或标签页)**的数据,刷新页面时不会删除,关闭窗口或标签页之后将会删除这些数据。 SessionStorage与LocalStorage对比: 都在本地进行数据存储; SessionStorage也有同源策略的限制,但是SessionStorage有一条更加严格的限制,SessionStorage只有在同一浏览器的同一窗口下才能够共享; LocalStorage和SessionStorage都不能被爬虫爬取 SessionStorage的使用场景 由于SessionStorage具有时效性,所以可以用来存储一些网站的游客登录的信息,还有临时的浏览记录的信息。当关闭网站之后,这些信息也就随之消除了。 常用API同上
Cookie有哪些字段,作用分别是什么
name=value 键值对,设置 Cookie 的名称及相对应的值,都必须是字符串类型- 如果值为 Unicode 字符,需要为字符编码。- 如果值为二进制数据,则需要使用 BASE64 编码。 domain 指定 cookie 所属域名,默认是当前域名 path 指定 cookie 在哪个路径(路由)下生效,默认是 ‘/’。如果设置为 /abc,则只有 /abc 下的路由可以访问到该 cookie,如:/abc/read。 maxAge cookie 失效的时间,单位秒。如果为整数,则该 cookie 在 maxAge 秒后失效。如果为负数,该 cookie 为临时 cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 cookie 。如果为 0,表示删除该 cookie 。默认为 -1。- 比 expires 好用。 expires 过期时间,在设置的某个时间点后该 cookie 就会失效。一般浏览器的 cookie 都是默认储存的,当关闭浏览器结束这个会话的时候,这个 cookie 也就会被删除。 secure 该 cookie 是否仅被使用安全协议传输。安全协议有 HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。 httpOnly 如果给某个 cookie 设置了 httpOnly 属性,则无法通过 JS 脚本 读取到该 cookie 的信息,但还是能通过 Application 中手动修改 cookie,所以只是在一定程度上可以防止 XSS 攻击,不是绝对的安全。
Cookie、LocalStorage、SessionStorage、IndexedDB区别
共同点: 都是前端本地存储的方式,均保存在浏览器端且同源 区别: (1)数据和服务器之间的交互方式 cookie可以在客户端和服务器之间来回传递 localStorage和sessionStorage不会主动把数据发送给服务器,仅在本地保存。 (2)生命周期 cookie在设置的有效期到期前都有效,默认是关闭浏览器后失效 sessionStorage仅在当前网页会话下有效,关闭页面或浏览器后失效 localStorage除非被手动清除,否则始终有效,永久保存 (3)存放数据大小 cookie存储容量较小,一般只有4KB localStorage和sessionStorage可以保存5MB的信息 (4)作用域 cookie和localStorage在所有同源窗口都是共享的 sessionStorage能被同一个窗口的同源页面所访问共享 其他: IndexedDB: 是被正式纳?HTML5标准的数据库储存?案,它是浏览器提供的一种本地的数据库存储机制,属于NoSQL数据库,?键值对进?储存,可以进?快速读取操作,?常适合web场景,同时?JavaScript进?操作会?常便。
IndexedDB有哪些特点?
键值对储存:IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。 异步:IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。 支持事务:IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。 同源限制:IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库; 储存空间大:一般来说不少于 250MB,甚至没有上限。 支持二进制储存:IndexedDB 不仅可以储存字符串,还可以储存二进制数据;
session
session 是另一种记录服务器和客户端会话状态的机制 session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中 session认证过程: 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
cookie作用
保存用户登录状态:一段时间内免登录 跟踪用户行为:天气预报网站中保存用户上次访问时的地区; 有换肤功能的网站保存用户上次访问的界面风格
session cookie区别
共同点: 都是用来跟踪浏览器用户身份的会话方式 区别: 安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。 存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。 有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。 存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。
cookie session token JWT https://mubu.com/doc/12i79Sq9hmP /* 2017/02/20 cookie操作 */ function setCookie(key, value, iDay) { var oDate = new Date(); oDate.setDate(oDate.getDate() + iDay); document.cookie = key + ‘=’ + value + ‘;expires=’ + oDate;
} function removeCookie(key) { setCookie(key, ‘’, -1);//这里只需要把Cookie保质期退回一天便可以删除 } function getCookie(key) { var cookieArr = document.cookie.split(’; ‘); for(var i = 0; i < cookieArr.length; i++) { var arr = cookieArr[i].split(’=’); if(arr[0] === key) { return arr[1]; } } return false; }
cookie存取
Cookie 用于存储 web 页面的用户信息。 /* cookie操作 */ function setCookie(key, value, iDay) { var oDate = new Date(); oDate.setDate(oDate.getDate() + iDay); document.cookie = key + ‘=’ + value + ‘;expires=’ + oDate;
} function removeCookie(key) { setCookie(key, ‘’, -1);//这里只需要把Cookie保质期退回一天便可以删除 } function getCookie(key) { var cookieArr = document.cookie.split(’; ‘); for(var i = 0; i < cookieArr.length; i++) { var arr = cookieArr[i].split(’=’); if(arr[0] === key) { return arr[1]; } } return false; }
七、浏览器同源策略跨域
接口请求的方式ajax、axios、fetch
(1)AJAX Ajax 即“AsynchronousJavascriptAndXML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。Ajax的核心是JavaScript对象XmlHttpRequest,XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。 通过XMLHttpRequest对象,Web开发人员可以在页面加载以后进行页面的局部更新。 优点: 通过异步模式,提升了用户体验, 允许你根据用户事件来更新部分页面内容 可以无需刷新页面与服务器进行通信 缺点: 没有浏览历史,不能回退; 存在跨域问题(同源) SEO不友好,网页中的内容爬虫不到 (2)Fetch fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多。fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。 fetch的优点: 语法简洁,更加语义化 基于标准 Promise 实现,支持 async/await 更加底层,提供的API丰富(request, response) 脱离了XHR,是ES规范里新的实现方式 fetch的缺点: 1.fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。 2.fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: ‘include’}) 3.fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费 4.fetch没有办法原生监测请求的进度,而XHR可以 (3)Axios Axios 是一种基于Promise封装的HTTP客户端,其特点如下: 浏览器端发起XMLHttpRequests请求 node端发起http请求 支持Promise API 支持请求拦截和响应拦截、转换请求数据和响应数据、取消请求 自动转换json数据 支持防御XSRF,就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的
AJAX创建请求步骤
get:创建一个对象->设置请求参数->发送请求->监听请求成功后的状态变化 post:创建一个对象->设置请求参数->设置请求头->发送请求->监听请求成功后的状态变化
get post区别
从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。 从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。 从参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,不会显示出来,更适合传输敏感信息。 从幂等性的角度,GET是幂等的,而POST不是。(幂等表示执行相同的操作,结果也是相同的) 从TCP的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)
实现一个AJAX请求,使用promise封装AJAX
(1)XMLHttpRequest类型对象: var xhr = new XMLHttpRequest();
(2)open方法开启请求:xhr.open(method,url),第三个参数要求传入一个布尔值,默认true异步方式执行 xhr.open(“get”,“https://jsonplaceholder.typicode.com/users?id=1”); xhr.open(“post”,“https://jsonplaceholder.typicode.com/users”);
(3)setRequestHeader(header,value)方法设置请求头:get不用设置,post必须设置 xhr.setRequestHeader(“Content-Type”,“application/x-www-form-urlencoded”); xhr.setRequestHeader(“Content-Type”,“application/json”);
(4)xhr.send(body)用于发送HTTP请求: body: 在XHR请求中要发送的数据体,根据请求头中的类型进行传参。 如果是GET方法,无需设置数据体,可以传null或者不传参 xhr.send(“name=mh&age=18”);// post xhr.send(’{“name”:“bn”,“age”:23,“class”:1}’); xhr.send();// get
(5)readyState 该属性返回一个 XMLHttpRequest 代理当前所处的状态,由于 readystatechange 事件是在 xhr 对象状态变化时触发,会触发多次
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
if (this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
};
xhr.onerror = function() {
console.error(this.statusText);
};
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
xhr.send(null);
使用promise封装AJAX
function getJSON(url) {
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
xhr.onerror = function() {
reject(new Error(this.statusText));
};
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
xhr.send(null);
});
return promise;
}
实现一个fetch
Promise fetch(input[, init])返回一个promise对象 input定义要获取的资源;init,可选项,一个配置项对象,包含所有对请求的设置;
fetch('http://127.0.0.1:8888/fetch-server?vip=10', {
method: 'POST',
headers: {
name: 'atguigu'
},
body: 'username=admin&password=admin'
}).then(response => {
return response.json();
}).then(response => {
console.log(response);
});
同源策略
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。 什么是协议,域名,端口? 解释: 假如一个网址是 http://baidu.com:8080?user=name&pwd=password http:// 是协议 baidu.com 是域名(注意:前面加上“wwww”即www.baidu.com不是域名) 8080 是端口 user=name&pwd=password 是地址带的参数
axios是什么?
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,在浏览器中利用的是XMLHttpRequest对象发送请求,在nodejs中用的是http对象。 特点: ?1.基于 promise 的异步 ajax 请求库,支持promise所有的API 2.浏览器端/node 端都可以使用,浏览器中发送XMLHttpRequests请求 3.可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据 4.拦截请求和响应数据
axios.get/post/()基本格式
axios.get(url[, config]) axios.post(url[, data[, config]]) axios(config)
const btns = document.querySelectorAll('button');
axios.defaults.baseURL = 'http://127.0.0.1:8888';
btns[0].onclick = function () {
axios.get('/axios-server', {
params: {
id: 100,
vip: 1
},
headers: {
name: 'wanglu',
age: 1
}
}).then((value) => {
console.log(value);
})
}
btns[1].onclick = function () {
axios.post('/axios-server', {
username : 'hahh',
password: 19
}, {
params: {
id: 100,
vip: 2
},
headers: {
name: 'wanglu',
age: 2
}
})
}
btns[2].onclick = function () {
axios({
method: 'POST',
url: '/axios-server',
params: {
id: 100,
vip: 3
},
headers: {
name: 'wanglu',
age: 3
},
data: {
username: 'admin',
password: '3'
}
}).then(response => {
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.data);
})
}
(1) axios.all发送兵法请求
使用axios.all, 可以放入多个请求的数组. axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
axios.all([axios({
url:'http://123.207.32.32:8000/home/multidata'
}),axios({
url:'http://123.207.32.32:8000/home/data',
params:{
type:'sell',
page:5
}
})]).then(results => {
console.log(results);
})
(2)创建axios实例
当我们从axios模块中导入对象时, 使用的实例是默认的实例.当给该实例设置一些默认配置时, 这些配置就被固定下来了.但是后续开发中, 某些配置可能会不太一样 // 4.创建对应的axios实例
const instance1=axios.create({
baseURL:'http://123.207.32.32:8000',
timeout:5000
})
instance1({
url:'/home/multidata',
}).then(res=>{
console.log(res);
})
instance1({
url:'/home/data',
type:'sell',
page:1
}).then(res=>{
console.log(res);
})
(3)axios的封装
import axios from ‘axios’
export function request(config) {
return new Promise((reslove, reject) => {
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
})
instance(config).then(res => {
reslove(res)
}).catch(err => {
reject(err);
})
})
}
(4)axios拦截器
axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。
request.js中:
instance.interceptors.request.use(config=>{
config.data=qs.stringify(config.data);
return config
},err=>{
return err;
})
instance.interceptors.response.use(res=>{
return res.data
},err=>{
console.log(err);
})
return instance(config)
}
什么是跨域?
当前页面中的某个接口请求的地址和当前页面的地址如果协议、域名、端口其中有一项不同,就说该接口跨域了.
如何解决跨越问题
解决方案: jsonp(利用script标签没有跨域限制的漏洞实现。缺点:只支持GET请求) CORS(设置Access-Control-Allow-Origin:指定可访问资源的域名) postMessage(message, targetOrigin, [transfer])(HTML5新增API 用于多窗口消息、页面内嵌iframe消息传递),通过onmessage监听传递过来的数据 Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。 Node中间件代理 Nginx反向代理 各种嵌套iframe的方式,不常用。 日常工作中用的最对的跨域方案是CORS和Nginx反向代理
(1)CORS
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器「不同的域、协议或端口」请求一个资源时,资源会发起一个「跨域 HTTP 请求」。 CORS需要浏览器和服务器同时支持,整个CORS过程都是浏览器完成的,无需用户参与。因此实现CORS的关键就是服务器,只要服务器实现了CORS请求,就可以跨源通信了。 目前最常用的一种解决办法,通过设置后端,解决了同源限制 res.setHeader(‘Access-Control-Allow-Origin’, ‘*’); res.setHeader(“Access-Control-Allow-Methods”, “GET, PUT, OPTIONS, POST”); 浏览器将CORS分为简单请求和非简单请求: 简单请求不会触发CORS预检请求。若该请求满足以下两个条件,就可以看作是简单请求: 1)请求方法是以下三种方法之一: HEAD GET POST 2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 若不满足以上条件,就属于非简单请求了 在简单请求中,在服务器内,至少需要设置字段:Access-Control-Allow-Origin
什么是预检请求? 非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求。 浏览器会询问服务器,当前所在的网页是否在服务器允许访问的范围内,以及可以使用哪些HTTP请求方式和头信息字段,只有得到肯定的回复,才会进行正式的HTTP请求,否则就会报错。 预检请求使用的请求方法是OPTIONS,表示这个请求是来询问的。他的头信息中的关键字段是Orign,表示请求来自哪个源。除此之外,头信息中还包括两个字段: Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。 Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。 服务器在收到浏览器的预检请求之后,会根据头信息的三个字段来进行判断,如果返回的头信息在中有Access-Control-Allow-Origin这个字段就是允许跨域请求,如果没有,就是不同意这个预检请求,就会报错。
减少optionsi请求次数? OPTIONS请求次数过多就会损耗页面加载的性能,降低用户体验度,Access-Control-Max-Age:number。它表示预检请求的返回结果可以被缓存多久,在这个时间范围内,再次发送请求就不需要进行预检请求。 **cors中的cookie问题?**了解 默认情况下在跨域请求,浏览器是不带 cookie ,如果想要传递cookie,可以设置:3步
在请求中设置 withCredentials
xhr.withCredentials = true
axios.defaults.withCredentials = true;
Access-Control-Allow-Credentials 设置为 true
Access-Control-Allow-Origin 设置为非*
客户端:
const btn = document.querySelector('button');
btn.onclick = function(){
const x = new XMLHttpRequest();
x.open("GET", "http://127.0.0.1:8888/cors-server");
x.send();
x.onreadystatechange = function(){
if(x.readyState === 4){
if(x.status >= 200 && x.status < 300){
console.log(x.response);
}
}
}
}
server.js服务器:
app.all('/cors-server', (request, response) => {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Headers", '*');
response.setHeader("Access-Control-Allow-Method", '*');
response.send('hello CORS');
});
(2)JSONP
利用的原理是script标签可以跨域请求资源,将回调函数作为参数拼接在url中 发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。 例如: 1.把不同源服务器的请求地址写在script标签的src属性里 script src=“http://127.0.0.1:8888/jsonp-server”></script 2.在当前客户端页面的全局作用域下定义一个函数fun function handle(data){} 3.不同源服务器响应的数据必须是函数调用,发送给客户端的数据写在函数调用里,可以用模板字符串拼接变量
app.all('/jsonp-server', (request, response) => {
const data = {
name: '尚硅谷atguigu'
};
let str = JSON.stringify(data);
response.send(`handle(${str})`);
});
4.客户端fun函数解析服务器传来的数据 function handle(data){ console.log( data ) }
(3)postMessage 跨域
postMessage是html5引入的API,postMessage()方法允许来自不同源的脚本采用异步方式进行有效的通信. 用途: 1.页面和其打开的新窗口的数据传递 2.多窗口之间消息传递 3.页面与嵌套的 iframe 消息传递 **用法:postMessage(data,origin)**方法接受两个参数: data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。 origin: 协议+主机+端口号,也可以设置为"",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/" otherWindow.postMessage(message, targetOrigin, [transfer]) otherWindow:目标窗口(你想发送跨域消息的那个窗口),例如:iframe.contentWindow message 将要发送的数据 targetOrigin 目标窗口的地址(URL),或者字符串’'表示无限制、任何URL都允许发送
a.html:(domain1.com/a.html):
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
};
window.addEventListener('message', function(e) {
alert('data from domain2 ---> ' + e.data);
}, false);
</script>
b.html:(domain2.com/b.html):
<script>
window.addEventListener('message', function(e) {
alert('data from domain1 ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
}
}, false);
</script>
(4)nginx代理跨域
Nginx 是一款轻量级的 Web 服务器,也可以用于反向代理、负载平衡和 HTTP 缓存等。Nginx 使用异步事件驱动的方法来处理请求,是一款面向性能设计的 HTTP 服务器。 跨域问题:同源策略仅是针对浏览器的安全策略。服务器端调用HTTP接口只是使用HTTP协议,不需要同源策略,也就不存在跨域问题。 实现思路:通过Nginx配置一个代理服务器域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名,由后端设置的域名domain2转换成你的域名domain1
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
(5)nodejs 中间件代理跨域
(6)document.domain + iframe跨域 (7)location.hash + iframe跨域 (8)window.name + iframe跨域
(9)WebSocket协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯
正向代理和反向代理的区别
正向代理: 客户端想获得一个服务器的数据,但是因为种种原因无法直接获取。于是客户端设置了一个代理服务器,并且指定目标服务器,之后代理服务器向目标服务器转交请求并将获得的内容发送给客户端。实现正向代理需要修改客户端,比如修改浏览器配置。 反向代理: 服务器为了能够将工作负载分不到多个服务器来提高网站性能 (负载均衡)等目的,当其受到请求后,会首先根据转发规则来确定请求应该被转发到哪个服务器上,然后将请求转发到对应的真实服务器上。一般代理是指代理客户端,这里的代理是代理服务器。一般使用反向代理后,需要通过修改 DNS 让域名解析到代理服务器 IP,这时浏览器无法察觉到真正服务器的存在,当然也就不需要修改配置了。 为什么要nginx反向代理? 1)安全及权限。可以看出,使用反向代理后,用户端将无法直接通过请求访问真正的内容服务器,而必须首先通过Nginx。可以通过在Nginx层上将危险或者没有权限的请求内容过滤掉,从而保证了服务器的安全。 2)负载均衡。例如一个网站的内容被部署在若干台服务器上,可以把这些机子看成一个集群,那么Nginx可以将接收到的客户端请求“均匀地”分配到这个集群中所有的服务器上(内部模块提供了多种负载均衡算法),从而实现服务器压力的负载均衡。
对 WebSocket 的理解
WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 原理: 客户端向 WebSocket 服务器通知(notify)一个带有所有接收者ID(recipients IDs)的事件(event),服务器接收后立即通知所有活跃的(active)客户端,只有ID在接收者ID序列中的客户端才会处理这个事件。 特点: 支持双向通信,实时性更强 可以发送文本,也可以发送二进制数据‘’ 建立在TCP协议之上,服务端的实现比较容易 数据格式比较轻量,性能开销小,通信高效 没有同源限制,客户端可以与任意服务器通信 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
websokect与AJAX的区别
生命周期不同: websocket 是长连接,会话一直保持 ajax 发送接收之后就会断开 适用范围: websocket 用于前后端实时交互数据 ajax 非实时 发起人: AJAX 客户端发起 WebSocket 服务器端和客户端相互推送
|