一、当用户在浏览器地址栏中输入网址,到看到页面,经历的步骤
tips:hexo自己搭一个博客

1.解析输入的URL地址
2.DNS解析
网站中,每发送一个TCP请求,都要进行DNS解析(一但当前域名解析过一次,浏览器一般会缓存解析记录,缓存时间一般在1分钟左右,后期发送的请求如果还是这个域名,则跳过解析步骤 =>这是一个性能优化点)
真实项目中,一个大型网站,他要请求的资源是分散到不同的服务器上的(每一个服务器都有自己的一个域名解析)
- WEB服务器(处理静态资源文件,例如:html/css/js等 的请求)
- 数据服务器(处理数据请求)
- 图片服务器 (处理图片请求)
- 音视频服务器
- ......
这样导致,我们需要解析的DNS会有很多次
优化技巧:DNS Prefetch 即 DNS 预获取
让页面加载(尤其是后期资源的加载)更顺畅更快一些
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//static.360buyimg.com">
<link rel="dns-prefetch" href="//misc.360buyimg.com">
<link rel="dns-prefetch" href="//img10.360buyimg.com">
<link rel="dns-prefetch" href="//img11.360buyimg.com">
<link rel="dns-prefetch" href="//img12.360buyimg.com">
3.基于TCP的三次握手,够建客户端和服务器端的连接通道
只有建立好连接通道,才能基于HTTP等传输协议,实现客户端和服务器端的信息交互

4.发送HTTP请求
基于HTTP等传输协议,客户端把一些信息传递给服务器
5.服务器接受到请求,并进行处理,最后把信息返回给客户端
- HTTP响应报文(所有服务器返回给客户端的内容)
- 响应起始行
- 响应首部(响应头)
- 响应主体
- 服务器返回的时候是:先把响应头信息返回,然后继续返回响应主体中的内容(需要的信息大部分都是基于响应主体返回的)
6.断开TCP链接通道 (四次挥手)

- 当客户端把请求信息发送给服务器的时候,就挥第一次手:客户端告诉服务器端,我已经把请求报文都给你了,你准备关闭吧
- 第二次挥手:由服务器发起,告诉浏览器,我接收完请求报文,我准备关闭,你也准备吧;
- 第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完毕,你准备关闭吧;
- 第四次挥手:由浏览器发起,告诉服务器,我响应报文接收完毕,我准备关闭,你也准备吧;
Connection: Keep-Alive 保持TCP不中断(性能优化点,减少每一次请求还需要重新建立链接通道的时间)
7.客户端渲染服务器返回的结果
二、前端性能优化点
1. 减少HTTP请求的次数和大小
- 合并压缩 webpack(代码比较少的情况下,尽可能使用内嵌式)
- 雪碧图或者图片BASE64
- 尽量使用字体图标
- 对于动态获取的图片,采用图片懒加载(数据也做异步分批加载:开始只请求加载第一屏的数据,滑动到第几屏在加载这一屏的数据和图片)
- 骨架屏技术(首屏内容由服务器渲染;再或者开始展示占位结构,客户端在单独获取数据渲染;)
- 音视频取消预加载(播放的时候再去加载音视频文件,对于自动播放采取延迟播放的处理)
- 服务器采用GZIP压缩
2.建立缓存机制
把一些请求回来的信息进行本地存储(缓存存储),在缓存有效期内,再次请求资源,直接从缓存中获取数据,而不是服务器上从新拉取
- DNS预获取
- 资源文件的强缓存和协商缓存(304)
- 数据也可以做缓存(把从服务器获取的数据存储到本地:cookie/localStorage/redux/vuex等,设定期限,在期限内,直接从本地获取数据即可)
- 离线存储(一般很少用)manifest
- CDN区域分布式服务器开发部署(费钱 效果会非常的好)
- ....
3.代码上的优化
- 减少DOM的重绘和回流
- 在JS中尽量减少闭包的使用(内存优化)
- 在JS中避免“嵌套循环”和“死循环”
- 尽可能使用事件委托
- 尽量减少CSS表达式的使用(expression)
- CSS选择器解析规则是从右向左解析(基于less/sass开发的时候尽可能减少层级嵌套,目的是让选择器的前缀短一点) 【 a{} 和 .box a{}】
- 尽可能实现JS的封装(低耦合高内聚),减少页面中的冗余代码
- 在CSS导入的时候尽量减少使用@import导入式
- 使用window.requestAnimationFrame(JS中的帧动画)代替传统的定时器动画(能用CSS3动画的绝对不用JS动画)
- 减少递归的使用,避免死递归,避免由于递归导致的栈内存嵌套
- 基于SCRIPT调取JS的时候,可已使用 defer或者async 来异步加载
……
4.安全优化
5.webpack上的优化
三、AJAX基础知识
AJAX:async javascript and xml 异步的JS和XML
发送AJAX请求有四步操作
GET系列 VS POST系列
let xhr = new XMLHttpRequest;
xhr.open('get', './data.json?lx=1&name=zhufeng');
xhr.send();
xhr = new XMLHttpRequest;
xhr.open('post', './data.json');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('lx=1&name=zhufeng&obj=' + encodeURIComponent(JSON.stringify({
name: '哈哈'
})));
xhr = new XMLHttpRequest;
xhr.open('post', './data.json');
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
let formData = new FormData();
formData.append('lx', 2);
formData.append('name', 'zhufeng');
formData.append('obj', {
name: '哈哈'
});
xhr.send(formData);
-
传递给服务器的数据格式:
- application/x-www-form-urlencoded:xxx=xxx&xxx=xxx (最常用的方式) 【字符串】
- multipart/form-data (也很常用,例如:表单提交或者文件上传) 【对象】
- raw (可以上传text、json、xml、html等格式的文本,富文本编辑器中的内容可以基于这种格式传递)
- binary (上传二进制数据或者编码格式的数据)
get和post的区别
-
GET请求传递给服务器的信息有大小的限制(因为它是基于地址问号传参方式传递信息,而URL有长度的限制:IE浏览器只有2KB大小...);而POST请求理论上是没有大小限制的(实际操作中也都会给予限制);
-
GET请求相对POST请求来说不太安全,也是因为传参是基于地址栏问号传参,会被别人基于URL劫持的方式把信息获取到...所以真实项目中,涉及到安全的信息(例如:密码等)都是基于POST方式传递的(互联网面前人人都在裸奔,没有绝对的安全,我们需要更多的处理安全性)
-
GET请求容易产生缓存,原因还是因为GET是基于问号传参传递信息的,浏览器在每一次获取数据后,一般会缓存一下数据,下一次如果请求的地址和参数和上一次一样,浏览器直接获取缓存中的数据,所以我们基于GET发送请求,需要清除缓存的时候,一般都会在地址栏中添加一个随机数
xhr.open('get', './data.json?lx=1&name=zhufeng&_='+Math.random())
-
AJAX状态码
0 =>unset
1 =>opened
2 => headers_Received
3 => loading
4 => done
let xhr = new XMLHttpRequest();
xhr.open("get", "./data.json");
// xhr.timeout = 100; 设置超时时间
// xhr.withCredentials=true; 跨域资源共享中,允许携带资源凭证
// xhr.abort() 强制中断AJAX请求
// xhr.setRequestHeader() 设置请求头信息(记住:属性值不能是中文和特殊字符)
xhr.setRequestHeader("name", encodeURIComponent("hello"));
xhr.onreadystatechange = function () {
let status = xhr.status,
state = xhr.readyState,
result = null;
if (!/^(2|3)\d{2}$/.test(status)) {
// 错误处理
return;
}
// AJAX状态码为2的时候,响应头信息回来了
if (state === 2) {
// 获取响应头信息
console.log(xhr.getAllResponseHeaders());
// console.log(xhr.getResponseHeader('date')); //=>获取的服务器日期是格林尼治时间 GMT(比北京时间晚了八个小时 北京时间:GMT+0800)
console.log(new Date(xhr.getResponseHeader("date"))); //=>转换为北京时间
return;
}
if (state === 4) {
// 获取响应主体信息 responseText/responseType/responseXML
result = xhr.response;
console.log(result);
}
};
xhr.send();
/* SEND后:首先响应头信息回来 最后响应主体信息再回来 */
HTTP状态码
-
状态码:AJAX状态码 \ 服务器返回的HTTP网络状态码(代表了服务器返回信息的状态)
[2开头的基本都是代表成功]
+200 OK 正常返回数据
[3开头的一般也是成功了,只不过中间做了一些额外处理]
- 301 Moved Permanently 永久性转移/重定向 一般应用于网站域名更换,访问老域名,永久都跳转到新的域名上
- 302 Move Temporarily 临时转移
- 307 Temporary Redirect 临时重定向 一般应用于服务器的负载均衡
- 304 Not Modified 读取的是缓存中的数据 这个是客户端和服务器端共建的协商缓存(把不经常更新,请求过的资源文件做缓存,后期在访问这些资源直接走缓存数据,除非服务器端更新了此资源,或者客户端强制清缓存刷新等)
[4开头的都是失败:失败的原因一般都是客户端的问题]
- 400 Bad Request 请求参数错误
- 401 Unauthorized 无权限访问
- 404 Not Found 地址错误
- 405 Method Not Allowed 当前请求的方式服务器不支持
[5开头的都是失败:失败的原因一般都是服务器问题]
- 500 Internal Server Error 未知服务器错误
- 503 Service Unavailable 服务器超负荷
常用的AJAX库(调用起来更方便、操作起来更简单):
-
以JQ/ZEPTO为主的AJAX库(把AJAX四步操作进行封装、提供了JSONP跨域处理、对于一些常用操作[例如:GET/POST/表单序列化/参数处理等]进行封装处理)
-
以AXIOS为主的AJAX库(不仅对AJAX进行封装,而且是基于PROMISE进行管理) [最常用的]
-
ES6中新增的内置类Fetch来完成HTTP数据请求(记住:FETCH不是AJAX,他是新的通讯方案;而且默认基于PROMISE进行管理) [很多公司已经开始尝试使用]
-
跨域请求方案...
AXIOS
axios.request({
// 请求的地址 baseURL:统一基本地址,一般是设置地址的前缀
url: "./data.json",
// 请求的方式
method: "GET",
// 设置请求头信息
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
// GET请求下,问号传参的信息
params: {},
// POST请求下,请求主体传参的信息
data: {},
})
.then((result) => {
// 从服务器获取的结果
/*
* config:你自己的配置信息
* headers:响应头信息
* request:原生的XHR AJAX对象
* status:网络状态码
* statusText
* data:存储的是响应主体信息
*/
console.log(result);
});
- 快捷请求的方法 axios.get/delete/head/post/put... 或者 axios.all
.get([URL],[OPTIONS])
axios.get("./data.json", {
params: {
lx: 0,
},
/* transformResponse: [function (data) {
// 把从服务器获取的请求主体信息进行处理,处理完在执行THEN操作
}] */
});
// .get([URL],[DATA],[OPTIONS]) [DATA]是基于请求主体传递的内容
// axios基于请求主体传递给服务器的默认是JSON格式,如果想变为x-www-form-urlencoded格式还需要自己处理
axios.post('./data.json', {
lx: 0,
name: 'zhufeng'
}, {
// 针对于POST系列请求,把传递给服务器请求主体内容进行处理(函数中返回的结果就是最后基于请求主体传递给服务器的内容)
transformRequest: [function (data) {
// xxx=xxx&xxx=xxx
let str = ``;
for (let key in data) {
if (!data.hasOwnProperty(key)) break;
str += `&${key}=${data[key]}`;
}
str = str.substring(1);
return str;
}],
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
|