本小节主要记录了网页中一些其余的知识点:javascript
浏览器的核心是两部分:渲染引擎和 JavaScript引擎。html
渲染引擎的主要做用是,将网页代码渲染为用户视觉能够感知的平面文档。渲染引擎处理网页,一般分红四个阶段:java
- 解析代码:HTML代码解析为 DOM,CSS代码解析为 CSSOM(CSS Object Model)。
- 对象合成:将 DOM和 CSSOM合成一棵渲染树。
- 布局:计算出渲染树的布局(layout)。
- 绘制:将渲染树绘制到屏幕。
以上四步并不是严格按顺序执行,每每第一步还没完成,第二步和第三步就已经开始了。因此会看到这种状况:网页的HTML代码还没下载完,但浏览器已经显示出内容。web
来看一下很是重要的重流和重绘。渲染树转换为网页布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具备阻塞效应,而且会耗费不少时间和计算资源。json
页面生成之后,脚本操做和样式表操做都会触发“重流”(reflow)和“重绘”(repaint)。用户的互动也会触发重流和重绘,如改变窗口大小、页面滚动、设置鼠标悬停
a:hover
效果、在输入框中输入文本等。api重流和重绘并不必定一块儿发生,重流必然致使重绘,重绘不必定须要重流。好比改变元素颜色,只会致使重绘,而不会致使重流;改变元素的布局,则会致使重绘和重流。大多数状况下,浏览器会智能判断,将重流和重绘只限制到相关的子树上面,最小化所耗费的代价,而不会全局从新生成网页。跨域
做为开发者,应该尽可能设法下降重绘的次数和成本。好比,尽可能不要变更高层的 DOM 元素,而以底层 DOM 元素的变更代替;再好比重绘
table
布局和flex
布局,开销都会比较大。浏览器
JavaScript引擎的主要做用是,读取网页中的JavaScript代码,对其处理后运行。早期浏览器内部对 JavaScript 的处理过程以下:缓存
- 对代码进行词法分析,将代码分解成词元。
- 对词元进行语法分析,将代码整理成语法树。
- 使用翻译器,将代码转为字节码。
- 使用解释器,将字节码转为机器码。
逐行所有解释将字节码转为机器码,是很低效的。为了提升运行速度,现代浏览器改成采用“即时编译”(Just In Time compiler,缩写 JIT),即字节码只在运行时编译,用到哪一行就编译哪一行,而且把编译结果缓存(inline cache)。一般一个程序被常常用到的只是其中一小部分代码,有了缓存的编译结果,整个程序的运行速度就会显著提高。服务器
字节码不能直接运行,而是运行在一个虚拟机之上,通常也把虚拟机称为JavaScript引擎。并不是全部的 JavaScript虚拟机运行时都有字节码,有的JavaScript虚拟机基于源码,即只要有可能,就经过 JIT编译器直接把源码编译成机器码运行,省略字节码步骤。这样作的目的,是为了尽量地优化代码、提升性能。
浏览器加载 JavaScript 脚本,主要经过<script>
元素完成。正常的网页加载流程是这样的:
- 渲染引擎一边下载HTML网页,一边开始解析。
- 解析过程当中发现
<script>
元素后暂停解析,把网页的控制权转交给JavaScript引擎。- JavaScript引擎下载脚本后执行代码。
- 执行完毕后,把网页控制权交还渲染引擎,恢复往下解析 HTML 网页。
浏览器会并行下载脚本,可是执行仍是按照定义的顺序来执行。
较好的作法是将<script>
标签都放在页面底部而不是头部的缘由以下:
对于来自同一个域名的资源,好比脚本文件、样式表文件、图片文件等,浏览器通常有限制,同时最多下载6~20个资源,即最多同时打开的 TCP 链接有限制,这是为了防止对服务器形成太大压力。若是是来自不一样域名的资源,就没有这个限制。因此,一般把静态文件放在不一样的域名之下,以加快下载速度。
在<script>
标签中还常见以下属性:
defer
属性的做用是延迟脚本的执行,等到 DOM 加载生成后再执行脚本。该属性的运行流程以下:
- 渲染引擎一边下载HTML网页,一边开始解析。
- 解析过程当中,发现带有
defer
属性的<script>
标签,继续往下解析 HTML 网页,同时开启另外一个进程并行下载<script>
元素加载的外部脚本。- 渲染引擎完成解析 HTML 网页,此时再交由JavaScript引擎执行已经下载完成的脚本。
defer
属性能够保证脚本下载的同时,浏览器继续渲染。须要注意的是,一旦采用这个属性,依旧保证脚本的执行顺序。
async
属性的做用是使用另外一个进程下载脚本,下载时不会阻塞渲染。该属性的运行流程以下:
- 渲染引擎一边下载HTML网页,一边开始解析。
- 解析过程当中,发现带有
async
属性的<script>
标签,继续往下解析 HTML 网页,同时开启另外一个进程并行下载<script>
标签中的外部脚本。- 脚本下载完成,渲染引擎暂停解析 HTML 网页,交由JavaScript引擎执行。
- 脚本执行完毕,JavaScript引擎交由渲染引擎恢复解析 HTML 网页。
async
属性能够保证脚本下载的同时,浏览器继续渲染。须要注意的是,一旦采用这个属性,就没法保证脚本的执行顺序。
通常来讲,若是脚本之间有依赖关系,就使用defer
属性;若是脚本之间没有依赖关系,就使用async
属性。若是同时使用和defer
和async
属性,浏览器行为由async
属性决定。
window
对象指当前的浏览器窗口。它也是当前页面的顶层对象,即最高一层的对象,全部其余对象都是它的下属。
网页对象属性
window.document
:指向document
对象window.location
:指向Location
对象window.localStorage
:指向本地储存的localStorage数据window.sessionStorage
:指向本地储存的sessionStorage数据工具对象属性:
window.locationbar
:地址栏对象,对象的visible
属性是一个布尔值,表示该组件是否可见。window.menubar
:菜单栏对象,对象的visible
属性是一个布尔值,表示该组件是否可见。window.toolbar
:工具栏对象,对象的visible
属性是一个布尔值,表示该组件是否可见。window.statusbar
:状态栏对象,对象的visible
属性是一个布尔值,表示该组件是否可见。window.onload
属性事件发生在文档在浏览器窗口加载完毕时
Location
对象是浏览器提供的原生对象,提供URL相关的信息和操做方法。经过window.location
和document.location
属性,能够拿到这个对象。
Location.href
:整个 URL。若是对它写入新的 URL 地址,浏览器会马上跳转到这个新地址Location.origin
:URL 的协议、主机名和端口。Location.protocol
:当前URL的协议,包括冒号Location.hostname
:当前URL的主机(不包括端口)。而Location.host包括端口Location.port
:当前URL的端口号Location.pathname
:URL 的路径部分,从根路径/
开始Location.search
:查询字符串部分,从问号?
开始。encodeURI()
方法用于转码整个URL。它的参数是一个字符串,表明整个 URL。decodeURI()
方法用于整个URL的解码。它接受一个参数,就是转码后的URL。Cookie是服务器保存在浏览器的一小段文本信息,浏览器每次向服务器发出请求,就会自动附上这段信息。不一样浏览器对Cookie数量和大小的限制是不同的。通常来讲单个域名设置的 Cookie 不该超过30个,每一个Cookie的大小不能超过4KB。超过限制之后Cookie将被忽略,不会被设置。
Cookie 有一些主要用途:
cookie除了使用K-V形式设置Cookie的名字Cookie的值外,它还有如下属性:
Expires
属性指定一个具体的到期时间,到了指定时间之后,浏览器就再也不保留这个Cookie。它的值是UTC格式,可使用Date.prototype.toUTCString()
进行格式转换。若是不设置该属性或设为null
,Cookie只在当前会话有效,浏览器窗口一旦关闭该Cookie就会被删除。Max-Age
属性指定从如今开始Cookie存在的秒数,好比60 * 60 * 24 * 365
(即一年)。过了这个时间之后,浏览器就再也不保留这个 Cookie。若是同时指定了Expires
和Max-Age
,那么Max-Age
的值将优先生效。Domain
属性指定浏览器发出HTTP请求时,哪些域名要附带这个Cookie。若是没有指定该属性,浏览器会默认将其设为当前域名,这时子域名将不会附带这个Cookie。若是指定了该属性,那么子域名也会附带这个 Cookie。Path
属性指定浏览器发出HTTP请求时,哪些路径要附带这个Cookie。只要浏览器发现,Path
属性是HTTP请求路径的开头一部分,就会在头信息里面带上这个Cookie。好比PATH
属性是/
,那么请求/docs
路径也会包含它。Secure
属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。HttpOnly
属性指定该Cookie没法经过JS脚本拿到,主要让document.cookie
、XMLHttpRequest
和Request API
拿不到该属性。这样就防止该Cookie被脚本读到。只有浏览器发出HTTP请求时,才会带上该Cookie。cookie的生成
在浏览器保存Cookie,就要在HTTP回应的头信息里面,放置一个或多个Set-Cookie
字段。一个Set-Cookie
字段可同时包括该cookie的多个属性,没有次序的要求。这个过程是服务端手动处理的。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
cookie的发送
向浏览器发送Cookie,就要在HTTP回应的头信息里面,放置一个或多个Cookie
字段。一个Cookie
字段可同时包含多个 Cookie,使用分号分隔。这个过程是浏览器自动处理的。
Cookie: name=value; name2=value2; name3=value3
Storage接口用于脚本在浏览器保存数据,保存的数据都以“键值对”的形式存在。sessionStorage
保存的数据用于浏览器的一次会话,当会话结束数据被清空;localStorage
保存的数据长期存在,下一次访问该网站的时候,网页能够直接读取之前保存的数据。除了保存期限的长短不一样,这两个对象的其余方面都一致。目前每一个域名的存储上限视浏览器而定,Chrome是2.5MB,Firefox和Opera是5MB。
StorageEvent.storageArea
:返回键值对所在的整个对象。也就是说能够从这个属性上拿到当前域名储存的全部键值对。StorageEvent.url
:表示原始触发storage事件的那个网页的网址。StorageEvent.key
:表示发生变更的键名。若是storage事件是由clear()
方法引发,该属性返回null
。StorageEvent.newValue
:表示新的键值。若是storage事件是由clear()
方法引发,该属性返回null
。StorageEvent.oldValue
:表示旧的键值。若是该键值对是新增的,该属性返回null
。Storage.key()
接受一个整数做为参数(从0开始),返回该位置对应的键值。Storage.getItem()
方法用于读取数据。它只有一个参数:键名。若是键名不存在,该方法返回null
。Storage.setItem()
方法用于存入数据。它接受两个参数:第一个是键名;第二个是保存的数据。若是键名已经存在,该方法会更新已有的键值。Storage.removeItem()
方法用于清除某个键名对应的键值。它接受键名做为参数,若是键名不存在,该方法不会作任何事情。Storage.clear()
方法用于清除全部保存的数据。所谓“同源”指的是“三个相同”:
- 协议相同
- 域名相同
- 端口相同
常见的跨源通行方式有以下:
WebSocket是一种通讯协议,使用ws://
(非加密)和wss://
(加密)做为协议前缀。该协议不实行同源政策,只要服务器支持,就能够经过它进行跨源通讯。具体使用状况后续再补充。
JSONP是服务器与客户端跨源通讯的经常使用方法。最大特色就是简单易用,老式浏览器所有支持,服务端改造很是小。去电就是JSONP只能发GET
请求。
第一步,网页添加一个<script>
元素,向服务器请求一个脚本,这不受同源政策限制,能够跨域请求。
<script src="http://api.foo.com?callback=bar"></script>
注意请求的脚本网址有一个callback
参数(?callback=bar
),用来告诉服务器客户端的回调函数名称。
第二步,服务器收到请求后,拼接一个字符串,将JSON数据放在函数名里面,做为字符串返回(bar({...})
)。
第三步,客户端会将服务器返回的字符串,做为代码解析。由于浏览器认为这是script
标签请求的脚本内容。这时客户端只要定义了bar()
函数,就能在该函数体内,拿到服务器返回的JSON数据。做为参数的JSON数据被视为 JavaScript对象而不是字符串,所以避免了使用JSON.parse
的步骤。
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://example.com/ip?callback=foo'); } function foo(data) { console.log('Your public IP address is: ' + data.ip); };
CORS是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,属于跨源AJAX请求的根本解决方法。它容许浏览器向跨域的服务器发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。相比JSONP只能发GET
请求,CORS容许任何类型的请求。
CORS通讯与普通的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求。所以实现 CORS 通讯的关键是服务器。只要服务器实现了CORS接口就可跨域通讯。
CORS 请求分红两类:简单请求和非简单请求。只要同时知足如下两大条件就属于简单请求,凡是不一样时知足上面两个条件就属于非简单请求。非简单请求是那种对服务器提出特殊要求的请求,好比请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
请求方法是如下三种方法之一:
- HEAD
- GET
- POST
HTTP的头信息不超出如下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
简单请求
简单请求发送:
对于简单请求,浏览器直接发出CORS请求。具体来讲就是在头信息之中,增长一个Origin
字段。
GET /cors HTTP/1.1 Host: api.alice.com Origin: http://api.bob.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
简单请求响应:
服务器收到查询请求之后,检查了Origin
字段之后,确认容许跨源请求就能够作出回应,返回的响应会多出几个头信息字段。若是服务器否认了查询请求,会返回一个正常的HTTP回应,可是没有任何CORS相关的头信息字段,或者明确表示请求不符合条件。浏览器发现这个回应的头信息没有包含Access-Control-Allow-Origin
字段就知道出错了,从而抛出一个错误。该错误被XMLHttpRequest
的onerror
回调函数捕获。注意这种错误没法经过状态码识别,由于HTTP回应的状态码有多是200。
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-
开头:
Access-Control-Allow-Origin
:该字段是必须的。它的值要么是请求时Origin
字段的值,要么是一个*
来表示接受任意域名的请求。注意若是服务器要求浏览器发送Cookie,该字段就不能设为星号,必须指定明确的、与请求网页一致的域名。Access-Control-Allow-Credentials
:该字段可选。表示是否容许发送Cookie。默认状况下,Cookie不包括在CORS请求之中。设为true
即表示服务器明确许可,浏览器能够把Cookie包含在请求中,一块儿发给服务器。设为true
同时要求开发者必须在AJAX请求中打开withCredentials
属性,不然即便服务器要求发送 Cookie,浏览器也不会发送。Access-Control-Expose-Headers
:该字段可选。CORS请求时XMLHttpRequest
对象的getResponseHeader()
方法只能拿到服务器返回头的6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。若是想拿到其余字段,就必须在Access-Control-Expose-Headers
里面指定。上面的例子指定getResponseHeader('FooBar')
能够返回FooBar
字段的值。
非简单请求
非简单请求发送:
非简单请求的CORS请求,会在正式通讯以前增长一次HTTP查询请求。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可使用哪些HTTP方法和头信息字段。只有获得确定答复后浏览器才会发出正式的XMLHttpRequest
请求,不然就报错。
这是为了防止这些新增的请求,对传统的没有CORS支持的服务器造成压力,给服务器一个提早拒绝的机会,这样能够防止服务器收到大量
DELETE
和PUT
请求,这些传统的表单不可能跨域发出的请求。
XMLHttpRequest
对象是AJAX的主要接口,用于浏览器与服务器之间的通讯。尽管名字里面有XML
和Http
,它实际上可使用多种协议(好比file
或ftp
),发送任何格式的数据(包括字符串和二进制)。下面是XMLHttpRequest
对象简单用法的完整例子:
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ // 通讯成功时,状态值为4 if (xhr.readyState === 4){ if (xhr.status === 200){ console.log(xhr.responseText); } else { console.error(xhr.statusText); } } }; xhr.onerror = function (e) { console.error(xhr.statusText); }; xhr.open('GET', '/endpoint', true); xhr.send(null);
XMLHttpRequest.withCredentials
属性是一个布尔值,表示跨域请求时用户信息(好比Cookie和认证的HTTP头信息)是否会包含在请求之中,默认为false
,即向example.com
发出跨域请求时,不会发送example.com
设置在本机上的Cookie(若是有的话)。若是须要跨域AJAX请求发送Cookie,须要withCredentials
属性设为true
。注意同源的请求不须要设置这个属性。
XMLHttpRequest.upload
属性返回一个对象,表示上传文件的相关事件。主要就是监听这个对象的各类事件。
XMLHttpRequest.timeout
属性返回一个整数,表示多少毫秒后,若是请求仍然没有获得结果就会自动终止。若是该属性等于0,就表示没有时间限制。
XMLHttpRequest.readyState
返回一个整数,表示实例对象的当前状态。它可能返回如下值:
- 0,表示XMLHttpRequest实例已经生成,可是实例的
open()
方法尚未被调用。- 1,表示
open()
方法已经调用,可是实例的send()
方法尚未调用,仍然可使用实例的setRequestHeader()
方法,设定HTTP请求的头信息。- 2,表示实例的
send()
方法已经调用,而且服务器返回的头信息和状态码已经收到。- 3,表示正在接收服务器传来的数据体(body 部分)。这时若是实例的
responseType
属性等于text
或者空字符串,responseText
属性就会包含已经收到的部分信息。- 4,表示服务器返回的数据已经彻底接收,或者本次接收已经失败。
XMLHttpRequest.status
属性返回一个整数,表示服务器回应的HTTP状态码。
XMLHttpRequest.responseType
属性是一个字符串,表示服务器返回数据的类型。
XMLHttpRequest.response
属性表示服务器返回的数据体(即HTTP回应的 body 部分),它多是任何数据类型(如字符串、对象、二进制对象等)。
XMLHttpRequest.responseURL
属性是字符串,表示发送数据的服务器的网址。
XMLHttpRequest.responseText
属性返回从服务器接收到的字符串。只有HTTP请求完成接收之后,该属性才会包含完整的数据。
XMLHttpRequest.ontimeout
:timeout事件的监听函数XMLHttpRequest.onloadstart
:loadstart事件(请求发出)的监听函数XMLHttpRequest.onprogress
:progress事件(正在发送和加载数据)的监听函数XMLHttpRequest.onloadend
:loadend 事件(请求完成,无论成功或失败)的监听函数XMLHttpRequest.onabort
:abort 事件(请求停止,如用户调用了abort()
方法)的监听函数XMLHttpRequest.onerror
:error 事件(请求失败)的监听函数XMLHttpRequest.onload
:load事件(请求成功完成)的监听函数XMLHttpRequest.onreadystatechange
:readystatechange事件(实例的readyState
属性变化)的监听函数XMLHttpRequest.open()
方法用于指定HTTP请求的参数,或者说初始化XMLHttpRequest实例对象。注意若是对使用过open()
方法的AJAX请求,再次使用这个方法,等同于调用abort()
,即终止请求。XMLHttpRequest.overrideMimeType()
方法用来指定MIME类型,覆盖服务器返回的MIME类型,从而让浏览器进行不同的处理。好比服务器返回的数据类型是text/xml
,因为种种缘由浏览器解析不成功报错,这时就拿不到数据。为了拿到原始数据能够把MIME类型改为text/plain
,这样浏览器就不会去自动解析,从而可拿到原始文本。注意该方法必须在send()
方法以前调用。XMLHttpRequest.setRequestHeader()
方法用于设置浏览器发送的HTTP请求的头信息。若是该方法屡次调用且设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。注意该方法必须在send()
方法以前调用。XMLHttpRequest.getResponseHeader()
方法返回HTTP头信息指定字段的值,若是尚未收到服务器回应或者指定字段不存在,则返回null
。XMLHttpRequest.send()
方法用于实际发出HTTP请求。XMLHttpRequest.abort()
方法用来终止已经发出的HTTP请求。调用这个方法之后,readyState
属性变为4,status
属性变为0。至于其余的还有File对象、Blob对象、ArrayBuffer对象等,到时候随用随查吧。