浏览器相关知识点

299 阅读14分钟

浏览器的同源策略

“同源”指什么

如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的

举个例子:

下表给出了相对 a.xyz.com/dir/page.ht… 同源检测的示例: 

URL结果原因
http://a.xyz.com/dir2/other.html成功 
http://a.xyz.com/dir/inner/another.html成功 
https://a.xyz.com/secure.html失败不同协议 ( https和http )
http://a.xyz.com:81/dir/etc.html失败不同端口 ( 81和80)
http://a.opq.com/dir/other.html失败不同域名 ( xyz和opq)

那些行为受到同源策略的限制

同源策略是什么

同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以xyz.com下的js脚本采用ajax读取abc.com里面的文件数据是会被拒绝的。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

不受同源策略限制的

  1. 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。

  2. 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的,,,等。

对于浏览器来说,除了DOM、Cookie、XMLHttpRequest 会受到同源策略的限制外,浏览器加载的一些第三方插件也有各自的同源策略。最常见的一些插件如 Flash ,有自己的控制策略。

常见的跨域方案有哪些

说的跨域,其实呢就是跨源。而跨域是一个统称,通过上面的我们知道了,因为同源策略,不同源之间,不能进行交互。那么跨域就是解决不同源之间发起请求、请求数据、发送数据、通信等交互问题解决方法的统称。

在浏览器中,<script><img> 、<iframe><link><video> 等标签都可以跨域加载资源,而不受同源策略的限制,通过 src 属性加载的资源,浏览器都会发起一个 GET 请求,但是浏览器限制了 JavaScript 的权限,使用js不能读、写加载的内容。

这句话什么意思呢,其实就是,你可以通过这几个标签来跨域加载资源,但是,发起的GET请求 返回的数据,通过 js 获取不到。

有如下常见的九种跨域方案: 1、JSONP跨域 2、跨域资源共享(CORS) 3、nginx代理跨域 4、nodejs中间件代理跨域 5、document.domain + iframe跨域 6、location.hash + iframe跨域 7、window.name + iframe跨域 8、postMessage跨域 9、WebSocket协议跨域

参考: 9种常见的前端跨域解决方案(详解)

浏览器的缓存相关

Web 缓存通常包括哪些

1、http缓存是基于HTTP协议的浏览器文件级缓存机制。 2、websql这种方式只有较新的chrome浏览器支持,并以一个独立规范形式出现 3、indexDB 是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API 4、Cookie一般网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密) 5、Localstoragehtml5的一种新的本地缓存方案,目前用的比较多,一般用来存储ajax返回的数据,加快下次页面打开时的渲染速度 6、Sessionstorage和localstorage类似,但是浏览器关闭则会全部删除,api和localstorage相同,实际项目中使用较少。 7、application cache 是将大部分图片资源、js、css等静态资源放在manifest文件配置中 8、cacheStorage是在ServiceWorker的规范中定义的,可以保存每个serverWorker申明的cache对象 9、flash缓存 这种方式基本不用,这一方法主要基于flash有读写浏览器端本地目录的功能

浏览器什么情况下会使用本地缓存

浏览器的本地存储主要分为Cookie、WebStorage和IndexedDB, 其中WebStorage又可以分为localStorage和sessionStorage。

强缓存和协商缓存的区别

对浏览器的缓存机制来做个简要的总结: 首先通过 Cache-Control 验证强缓存是否可用 如果强缓存可用,直接使用 否则进入协商缓存,即发送 HTTP 请求,服务器通过请求头中的If-Modified-Since或者If-None-Match字段检查资源是否更新 若资源更新,返回资源和200状态码 否则,返回304,告诉浏览器直接从缓存获取资源

参考:http协商缓存VS强缓存关于浏览器的缓存和本地存储及其优缺点

强制 ctrl + F5 刷新会发生什么

IE里F5和Ctrl+F5区别:一个是刷新,一个是强制刷新。按F5有时候一些内容是不会被更新的,而CTRL+F5则所有内容都会被更新.

具体区别是:F5通常只是刷新本地缓存;Ctrl+F5可以把INTERNET临时文件夹的文件删除再重新从服务器下载,也就是彻底刷新页面了

session、cookie 以及 storage 的区别

cookie的内容主要包括:名字、值、过期时间、路径和域。路径与域一起构成cookie的作用范围。若不设置时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就会消失。这种生命期为浏览器会话期的cookie被称为会话cookie。

会话cookie一般不存储在硬盘而是保存在内存里,当然这个行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再打开浏览器这些cookie仍然有效直到超过设定的过期时间。对于保存在内存里的cookie,不同的浏览器有不同的处理方式session机制。

当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。

参考:# Cookie、session和localStorage、以及sessionStorage之间的区别

浏览器加载顺序

为什么我们通常将 javascript 放在 的最后面

为什么我们将 CSS 放在 里

正常的网页加载流程是这样的:

  1. 浏览器一边下载 HTML 网页,一边开始解析
  2. 解析过程中,发现<script>标签
  3. 暂停解析,网页渲染的控制权转交给JavaScript引擎
  4. 如果<script>标签引用了外部脚本,就下载该脚本,否则就直接执行
  5. 执行完毕,控制权交还渲染引擎,恢复往下解析HTML网页

js放在body的最后面,可以避免资源阻塞,同时使静态的html页面迅速显示。
如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。
html需要等head中所有的jscss加载完成后才会开始绘制,但是html不需要等待放在body最后的js下载执行就会开始绘制。

css放在head里,可避免浏览器渲染的重复计算。
经过上面的渲染过程,我们知道Layout的计算是比较消耗性能的,所以我们在开始计算Render Tree之前,就把所有的css文件拿到,这样可减少RepaintReflow

浏览器的渲染原理

我们亲爱的浏览器会解析三个东西:

  1. HTML/SVG/XHTML。解析这三种文件会产生一个DOM Tree。(渲染引擎)
  2. CSS,解析CSS会产生CSS规则树。(渲染引擎)
  3. Javascript脚本,主要是通过DOM APICSSOM API来操作DOM TreeCSS Rule Tree。(JavaScript 解释器)

解析完成后,浏览器引擎会通过DOM TreeCSS Rule Tree来构造Render Tree

大致流程如下图: image

HTML/CSS/JS 的解析过程

html会生成这样的一个DOM Tree
image

DOM TreeRender Tree有个很简单的区别:像headerdisplay:none的元素,会在DOM Tree中,但不会添加到Render Tree里。

节点树中的节点彼此拥有层级关系。
父(parent)、子(child)和同胞(sibling)等术语用于描述这些关系。父节点拥有子节点。同级的子节点被称为同胞(兄弟或姐妹)。

  • 在节点树中,顶端节点被称为根(root
  • 每个节点都有父节点、除了根(它没有父节点)
  • 一个节点可拥有任意数量的子
  • 同胞是拥有相同父节点的节点

通过HTML DOM,树中的所有节点均可通过JavaScript进行访问。所有HTML元素(节点)均可被修改,也可以创建或删除节点。

Render Tree 是怎样生成的

Render Tree

CSS Rule Tree主要是 Firefox 的产物。
Firefox 基本上来说是通过CSS解析生成CSS Rule Tree,然后通过比对DOM生成Style Context Tree
然后 Firefox 通过把Style Context Tree和其Render Tree(Frame Tree)关联上,就完成了。

Webkit 不像 Firefox 要用两个树来干这个,Webkit 也有Style对象,它直接把这个Style对象存在了相应的DOM结点上了。

建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSSSelector,这是一个相当复杂和有性能问题的事情。

Repaint 和 Reflow 是怎样的过程

1. 重绘(Repaint)
屏幕的一部分要重画,比如某个 CSS 的背景色变了。但是元素的几何尺寸没有变。

2. 重排(Reflow)
元件的几何尺寸变了(Render Tree的一部分或全部发生了变化,ReflowLayout),需要重新验证并计算Render Tree
HTML使用的是流式布局,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫Reflow

Reflow会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,成本比Repaint的成本高得多的多。

日常开发中要注意哪些渲染性能问题

要注意以下一些操作,因为可能会导致性能降低:

  • 增加、删除、修改DOM结点
  • 移动DOM的位置,或是搞个动画
  • 修改CSS样式
  • Resize窗口(移动端没有这个问题),或是滚动

了解这些以后,我们在写代码的时候就会下意识去比避免啦。当然,现在 MVVM 框架流行,以及 CSS3 普遍之后,手动操作DOM的场景也越来越少啦。

虚拟 DOM 机制

为什么要使用虚拟 DOM

所以随着应用程序越来越复杂,DOM操作越来越频繁,需要监听事件和在事件回调用更新页面的 DOM 操作也越来越多,性能消耗则会比较大。于是乎,虚拟DOM的想法便被人提出并实现了。

虚拟DOM其实是用来模拟真实DOM的中间产物,主要包括以下功能:

1. 用JS对象模拟DOM树,简化DOM对象。

简单来说,就是用一个对象模拟,保留主要的一些DOM属性,其他的则去掉。

2. 使用虚拟DOM,结合操作DOM的接口,来生成真实DOM

使用假DOM生成真DOM,同时保持真实DOM对象的引用,以便 3 步骤的执行。

3. 更新DOM时,比较两棵虚拟DOM树的差异,局部更新真实DOM

这个就比较有意思,可以根据数据的变化,来最小化地移动、替换、删除原有的DOM元素。

结合使用以上功能,便能在复杂应用中更好地维护了。而我们现在很多的前端框架,例如 Angular、React、Vue 等,都为了给开发者提供便捷的数据绑定机制、高效的 DOM 更新机制而做了不少的工作,更多的可以参考《如何理解前端和 Vue》一文

为什么要使用 JS 对象来描述 DOM 结构

通过 HTML DOM,树中的所有节点均可通过JavaScript 进行访问。所有 HTML 元素(节点)均可被修改,也可以创建或删除节点。

DOM,简单点来说就是将你的客户端(IE/Google等)以树状结构从大到小拆分成单一的对象让你进行操作。通过JavaScript,可以重构整个HTML文档。可以自由的进行添加、移除、改变或者重排页面上的项目。只要想改变页面某个东西,都可以用JavaScript获得对HTML文档中所有元素进行访问的入口,而这些入口和对HTML元素进行添加、移动、改变或移除的方法和属性,都是通过DOM来获得的。也就是说,DOM可以帮助JavaScript能更好的和页面进行交互。

通俗的讲,DOM定义了表示和修改文档所需的对象、行为和属性,以及这些对象之间的关系。当你想要改变网页行为的时候你该怎么办呢?你不会用脚来踹网页或者用头来顶网页吧?这时候怎么办呢 ?正好你手中有JavaScript 这种工具!所以你就会考虑用JavaScript来改变网页行为,而这时候你会想 我怎么改变呢?而我发现 网页原来有接口的,就好像你推车有车柄一样,你可以作用于车柄让它往前走,同样的你也会作用于网页的接口改变他的行为,而这个接口就是DOM。所以 JavaScript和DOM 就好像你的手和车柄一样,你不会用你的头去撞车柄,同样你也不会用CSS去改变网页行为!当然手也不只是限于推车,还可以做别的事情,JavaScript也一样可以干别的事情!

所以通过比较HTML和DOM,我们可以发现,HTML的优点是方便直观,能较快速的掌握布局,缺点是无法动态地绑定局部事件,不能直接保存子元素以便以后的动态修改。但DOM操作正好可以弥补这些。

简单描述下虚拟 DOM 的实现原理

参考:[# Virtual DOM (虚拟DOM)的实现原理](blog.csdn.net/u012961419/…

浏览器事件

DOM 事件流包括几个阶段(点击后会发生什么)

事件流所描述的就是从页面中接受事件的顺序。
DOM 事件流(event flow)存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

  1. 捕获阶段:一开始从文档的根节点流向目标对象;
  2. 目标阶段:然后在目标对向上被触发;
  3. 冒泡阶段:之后再回溯到文档的根节点。

事件委托是什么

基于事件冒泡机制,我们可以实现将子元素的事件委托给父级元素来进行处理。
当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。

这样能解决什么问题呢?

  1. 绑定子元素会绑定很多次的绑定,而绑定父元素只需要一次绑定。
  2. 将事件委托给父节点,这样我们对子元素的增加和删除、移动等,都不需要重新进行事件绑定。

很常见的就是我们有个列表,每个选项都可以进行编辑、删除、添加标签等功能,而把事件委托给父元素或者document,不管我们新增、删除、更新选项,都不需手动去绑定和移除事件。

最常在jQuery中使用事件委托:

$("#my-list").delegate("button", "click", function() {
  // "$(this)"是被click的元素
  console.log("you clicked a button", $(this));
});

现在我们基本上都使用框架了,我们可以随意地在元素上绑定事件,如 Vue 中<div @click="myClickEvent" />,因为框架会帮我们用事件委托的方式处理掉,大部分都会绑定在最外层初始化的id元素,或者是document