Ajax学习第一天

•传统网站中存在的问题

  1. 网速慢导致的页面加载时间过长
  2. 表单重填问题
  3. 页面的跳转问题(有时候页面中只需要某个特定部分实现改变而使整个页面发生跳转)
    以上的问题导致用户体验不佳,故提出Ajax解决方案,它是浏览器提供的一套方法,实现页面无刷新更新数据,它是运行在客户端上的。

•应用场景

  1. 页面上拉加载更多数据

  2. 列表分页

  3. 表单验证

  4. 搜索框提示文字下拉列表

    •运行环境

  5. Ajax技术需要运行在网络环境中才能生效(以域名的方式进行访问)

•运行原理

在传统的浏览器端与服务器端直接进行交互之间增加Ajax 使开发人员对整个过程可控。

•实现步骤

  1. 创建Ajax对象var xhr = new XMLHttpRequest();

  2. 告诉Ajax请求地址及请求方式xhr.open('get','http://www.example.com');

  3. 发送请求xhr.send();

  4. 获取服务器端给与客户端的响应数据xhr.onload=function(){ console.log(xhr.responseText);}

    •注意事项
    在http请求与响应过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会转换为对象字符串进行传输。

1
JSON.parse(); //将json字符串转换为json对象

Ajax学习第二天

•请求参数传递

  1. Get请求方式
    请求参数需要自己拼接并且需要注意拼接的格式和方法 xhr.open('get','http://www.examples.com?name='zhangsan'&age=20');

  2. Post请求方式
    需要设置请求报文头xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    并且在send()函数中填写拼接的参数xhr.send('name='zhangsan'&age=20');

•请求报文
在HTTP请求和响应的过程中传递的数据块就叫做报文,包括要传送的数据和一些附加信息,这些数据和信息要遵守规定好的格式。

•请求参数的格式

  1. application/x-www-form-urlencoded
    name='张三' & age = 20 & sex = '男';
  2. application/json
    {name:'张三' , age: 20 , sex : '男'};
    在请求头中指定Content-Type属性的值是application/json 告诉服务器端当前请求参数的格式是json
    JSON.stringify() //将json对象转换为json字符串
    注意:get请求是不能提交json对象数据格式的,传统网站的表单提交也是不支持json对象数据格式的。

•获取服务器端的响应
Ajax状态码
在创建Ajax对象,配置Ajax对象,发送请求,以及接受完服务器端响应数据,这个过程中的每一个步骤都会对应一个数值,即为Ajax状态码

  • 0:请求未初始化(还未调用open)
  • 1:请求已经建立但是还未发送(还未调用send)
  • 2:请求已经发送
  • 3:请求正在处理中,通常响应中已有部分数据
  • 4:响应已经完成,可以获取并使用服务器响应了
1
xhr.readyState //获取Ajax状态码

onreadystatechange事件
当Ajax状态码发生变化时将自动触发该事件
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//注意:此方法目前已经不再流行
<script>
var xml = new XMLHttpRequest();
console.log(xml.readyState);
xml.open('get','localhost/readyState');
console.log(xml.readyState);
xml.onreadystatechange = function(){
console.log(xml.readyState);
if(xml.readyState == 4){
console.log(xml.responseText);
}
}
xml.send();
</script>

onload事件与onreadystatechange事件的区别

区别onload事件onreadystatechange事件
是否兼容IE低版本不兼容兼容
是否需要判断Ajax状态码不需要需要
被调用次数一次多次

•Ajax错误处理

  1. 网络畅通,服务器端能接收到请求,服务器端返回的结果不是预取结果
    解决办法:可以判断服务器端返回的状态码,分别进行处理,xhr.status可以用以获取http状态码;
  2. 网络畅通,服务器端没有接收到请求,返回404状态码
    解决办法:检查请求地址是否错误;
  3. 网络畅通,服务器端能接收到请求,服务器端能接收到请求,服务器端返回500状态码
    解决办法:服务器端错误,找后端开发人员进行沟通
  4. 网络中断,请求无法发送到服务器端
    解决办法:会触发xhr对象下面的onerror事件,在onerror事件处理函数中对错误进行处理

Ajax状态码与HTTP状态码的区别

区别含义返回值
Ajax状态码Ajax请求的进程状态Ajax对象返回的
HTTP状态码请求的处理结果服务器端返回的

Ajax学习第三天

今天主要围绕以下两个问题解决:

1. 低版本IE浏览器的缓存问题
     解决方案:在请求地址的后面加上请求参数,保证每一次请求中的请求参数的值不相同即可。
xhr.open('get','http://www.example.com?t='+Math.random());

2. 发送一次请求代码过多,发送多次请求代码冗余且重复
      解决方案:将请求代码封装到函数中,发请求时调用函数即可。封装Ajax代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<body>
<script>
function ajax(options) {
//存储的是默认值
var defaults = {
type: 'get',
url: '',
data: '',
header: {
'Content-Type': 'application/json'
},
success: function () { },
error: function () { }
};
//对象覆盖 使用options对象中的属性覆盖defaults对象中的属性
Object.assign(defaults, options);
//创建Ajax对象
var xhr = new XMLHttpRequest();
//定义需要拼接的字符串
var params = "";
//循环对象
for (var attr in defaults.data) {
//将参数转换为字符串
params = params + attr + '=' + defaults.data[attr] + "&";
}
//将最后一个&截取掉 得到完整的拼接的字符串
params = params.substring(0, params.length - 1);
//1.为get方式 2.为post方式
if (defaults.type == 'get') {
//指明请求方式及请求路径
xhr.open(defaults.type, defaults.url + '?' + params);
xhr.send();
} else if (defaults.type == 'post') {
//指明请求方式及请求路径
xhr.open(defaults.type, options.url);
//判断请求报文头的类型
var contentType = defaults.header['Content-Type'];
//设置请求报文头的类型
xhr.setRequestHeader('Content-Type', contentType);
//1.application/x-www-form-urlencoded类型
//2.application/json类型
if (contentType == 'application/x-www-form-urlencoded') {
xhr.send(params);
} else if (contentType == 'application/json') {
xhr.send(JSON.stringify(defaults.data));
}
}
//服务器端响应
xhr.onload = function () {
//得到服务器返回的类型
var contentType = xhr.getResponseHeader('Content-Type');
//服务器端返回的数据
var responseText = xhr.responseText;
//若为json类型
if (contentType.includes('application/json')) {
//转换为json对象 便于使用
responseText = JSON.parse(responseText);
}
//判断http状态码等于200为正常
if (xhr.status == 200) {
//请求成功 调用success函数
defaults.success(responseText, xhr);
} else {
//请求失败 调用error函数
defaults.error(responseText, xhr);
}

}
}

ajax({
type: 'post',
url: 'http//localhost/Ajax封装.html',
data: { name: 'zhangSan', age: 20 },
header: {
'Content-Type': 'application/json'
},
success: function (data, xhr) {
console.log(data);
console.log(xhr);
},
error: function (data, xhr) {
console.log(data);
console.log(xhr);
}
});


/*
请求参数要考虑的问题
1.请求参数位置的问题
将请求参数传递到Ajax函数内部,在函数内部根据请求方式的
不同将请求参数放置在不同位置

get 放在请求地址的后面
post 放在send()中
2.请求参数格式的问题
application/x-www-form-urlencoded
参数名称 = 参数值 & 参数名称 =参数值
name = zhangSan & age = 20
application/json
{name:'zhangSan',age: 20}

1.传递对象数据类型对于函数的调用者更加友好
2.在函数内部对象数据类型转换为字符串数据类型更加方便
*/
</script>
</body>

大致思路总结如下:

  1. 封装请求类型:判断请求类型为 get / post;

  2. 封装请求报文头类型:application/x-www-form-urlencoded   application/json
    若为后者,则需要在将拼接的字符串类型转换为JSON字符串类型,使用函数JSON.stringify();

  3. 封装服务器端返回结果:

    1. 判断http状态码,使用xhr.status 若状态码为200 则调用成功函数 若为其它,则调用错误函数。
    2. 一般情况下,服务器端返回JSON数据作为响应内容,但是客户端拿到的是JSON字符串,所以我们最好将JSON字符串转换为JSON对象,这样函数调用者方便修改数据。
      使用函数xhr.getResponseHeader('Content-Type');得到服务器返回的类型,若得到的是application/json类型,则将服务器返回的数据转换为JSON对象responseText = JSON.parse(responseText); 若得到text/html类型,则不做处理
  4. 封装用户是否传值:若用户设置相关内容,则使用用户设置的相关内容,否则使用默认值,要注意对象覆盖函数的用法!Object.assign(defaults, options);

Ajax学习第四天

今天主要是写了两个案例,这两个案例还是单独开一栏写好了,这里介绍一下模板引擎的使用。
作用:使用模板引擎提供的模板语法,可以将数据和HTML拼接起来,这里我使用 的是art-template,使用步骤如下:

  1. 下载 art-template 模板引擎库文件并在HTML页面中引入库文件

  2. 准备 art-template 模板

    1
    2
    3
    4
    <script type="text/html" id="tpl">
    <div id="box">
    </div>
    </script>
  3. 告诉模板引擎将哪一个模板和哪个数据进行拼接
    var html = template('tpl',{username:'zs',age:'20'});

  4. 将拼接好的html字符串添加到页面中
    document.getElementById('contaoner').innerHTML = html

  5. 通过模板语法告诉模板引擎,数据和html字符串要如何拼接

    1
    2
    3
    4
    5
    <script type="text/html" id="tpl">
    <div id="box">
    <h1>{{username}},{{age}}</h1>
    </div>
    </script>

Ajax学习第五天

FormData及同源政策

背景:由前几天的学习可以知道,当对表单数据进行提交的时候,Ajax需要我们指明需要提交的属性及数据并进行拼接,在这之前,我们必须手动获取对应控件的相关内容,一旦数据量过大,就很容易造成拼接错误等问题,所以提出了FormData这一解决方案。

  • FormData对象的作用

         1. 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式;
         2. 异步上传二进制文件(例如:图片 视频及音频文件)。
  • FormData对象的使用

    1. 准备HTML表单,不用设置请求地址及请求方式,也不用将input控件中的按钮设置为submit,设置为button即可,因为若设置为submit方式,表单会自动进行提交,从而使FormData不生效,值得注意的是,必须为需要提交数据的控件设置name属性

      1
      2
      3
      4
      <form id="form">
      <input type="text" name="username">
      <input type="password" name="password">
      <input type="button" id="btn" value="提交">
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80

      2. 利用FormData构造函数,将HTML表单转化为FormData对象
      ``var form = document.getElementById('form');``
      ``var formData = new FromData(form);``

      3. 提交表单对象,采用此方法提交表单时,**不支持get方法**
      ``xhr.send(formData);``

      - **FormData对象的实例方法**

      1. 获取表单对象中属性的值
      ``formData.get('key');``
      2. 设置表单对象中属性的值,**如果表单控件不存在,则会自动创建这个表单属性**
      ``formData.set('key','value');``
      3. 删除表单对象中属性的值
      ``formData.delete('key');``
      4. 向表单对象中追加属性值,**它可以在空的表单对象中追加值**
      ``formData.append('key','value');``
      **与 set 方法的区别**:在属性名已存在的情况下,set 方法会覆盖已有键名的值,append 会保留两个值,但是服务器端需要响应的设置,否则,仍然传递最后一次的值。

      - **FormData对二进制文件的上传及文件上传进度的展示**
      代码如下:


      ```javascript
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
      integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
      </head>
      <body>
      <div class="container">
      <div class="form-group">
      <label>请选择文件</label>
      <input type="file" id="file">
      <br/>
      <div class="progress">
      <div class="progress-bar" style="width: 0%;" id="progress">0%</div>
      </div>
      </div>
      </div>
      <script>
      //获取文件控件
      var file = document.getElementById('file');
      //对文件控件田间发生改变事件
      file.onchange = function () {
      //创建空的FormData构造函数
      var formData = new FormData();
      //将file控件中选择的文件追加到formData中
      // 第一个参数开发过程中由后端人员提供 第二个参数代表选择的文件
      formData.append('attrName',this.files[0]);
      //创建Ajax对象
      var xhr = new XMLHttpRequest();
      //配置Ajax对象
      xhr.open('post','http://localhost/foemData');
      //在文件上传的过程中持续触发
      xhr.upload.onprogress = function(ev){
      //ev.loaded 文件已经上传了多少
      //ev.total 上传文件总大小
      var pro = (ev.loaded / ev.total)*100+'%';
      //获取进度条
      var progress = document.getElementById('progress');
      //设置进度条的宽度
      progress.style.width = pro;
      //设置进度条的数值
      progress.innerHTML = pro;
      }
      xhr.send(formData);
      xhr.onload = function(){
      console.log(xhr.responseText);
      }
      }
      </script>
      </body>
      </html>
  • FormData文件上传图片即使预览
    在我们将图片上传到服务器以后,服务器通常都会将图片地址作为响应数据传递到客户端,客户端可以从响应数据中获取图片地址,然后将图片再显示到页面中,值得注意的是,客户端得到地址后,需要动态的创建img标签,即首先var img = document.createElement('img');,再采用box.appendChild(img);的方式添加到页面上,如果直接将图片地址赋值给img的src,客户会看到图片的加载过程,影响用户体验;在服务端,同样的需要设置,应该引入表单解析对象,即formidable。

接下来是有关昨天做行政区联动碰到的问题,大相径庭。

  • 同源政策
    Ajax的请求限制:Ajax只能向自己的服务器发送请求,比如说,A网站的服务器端有A的HTML文件,B网站的服务器端有B的HTML页面,此时,A可以向自己的服务器发送Ajax请求,B也可以向自己的服务器发送Ajax请求,但是A不能向B的服务器发送Ajax请求,B也同理。

  • 什么是同源
    如果两个页面拥有相同的域名,协议及端口,那么我们称这两个页面是同源的,其中只要有一个不同,都是不同源的。

  • 同源政策的目的
    同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据,最初的同源政策是指A网站在客户端防止的Cookie,B网站是不能访问的。

  • 使用JSONP解决同源限制问题
    JSONP:json with padding的简称 它不属于Ajax请求,但它可以模拟Ajax请求

  • 使用JSONP解决同源限制问题的步骤

    1. 将不同源的服务器端请求地址写在script标签的src属性中,因为script标签不受同源政策的影响
          ``<script src="www.example.com"></script>``
    2. 服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数
          ``const data = 'fn({name:'zhangSan',age:'20'})';``
            ``res.send(data);``
    3. 在客户端全局作用域下定义函数fn,这个函数需要写在script标签的前面
          ``function fn(data){   }``
    4. 在fn函数内部对服务器端返回的数据进行处理
          ``function fn(data){console.log(data)}``

Ajax学习第六天

Ajax学习第六天——JSONP代码优化,使用最初版本的JSONP代码,即昨天博客上写的步骤,会有以下3个问题:

  1. 使用JSONP方法时,每次都要将函数名称发送给服务器端,一旦服务器端程序员修改函数名,前端界面也要一起修改,造成了沟通成本的增加;

  2. 一旦出现有多个请求时,采用此时的JSONP代码会出现若第一个请求还未执行完成,发送第二个请求,服务器端会覆盖第一次请求的结果,导致第一次的请求结果被覆盖;

  3. 若多次调用JSONP,要重复编写大量相似且冗余的代码,比较繁琐。

    针对以上3个问题,提出以下3个要求:

  4. 无论怎么修改函数名,都不需要前端后端程序员重新沟通;

  5. 将script的请求的发送变成动态请求;

  6. 封装JSONP函数,方便请求发送。

实现以上三个要求的代码如下(本代码针对前端,服务器端代码未贴出):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<button id="btn">点我发送请求</button>
<script>
//获取按钮标签
var btn = document.getElementById('btn');
//为按钮添加点击事件
btn.onclick = function () {
jsonp({
url: 'http://localhost/jsonp',
data: {
name: 'zs',
age: 20
},
success: function (data) {
console.log(data);
}
});
}
function jsonp(options) {
//动态添加script标签 使发送请求这一动作变得可控,否则页面一上来就会被加载
var script = document.createElement('script');
//拼接字符串的变量
var params = '';
//遍历拿到数据
for (attr in options.data) {
params += '&' + attr + '=' + options.data[attr];
}
//函数名随机 否则如果有两个按钮同时点击响应同一个函数的话,后面函数返回结果会覆盖前面函数的返回结果
var attrName = 'script' + Math.random().toString().replace('.', '');
//它已经不是一个全局函数了
///我们要想办法让它重新变为全局函数 .后面不能是个变量哦!
window[attrName] = options.success;
//设置script标签的src属性
script.src = options.url + '?callback=' + attrName + params;
//动态添加script标签
document.body.appendChild(script);
//为script标签添加onload事件 此事件是页面全部加载完毕后才触发
script.onload = function () {
//页面加载完毕后移除script标签,因为执行完毕后此标签已没有意义
document.body.removeChild(script);
}
}
</script>
</body>

</html>

Ajax学习第七天

Ajax学习第七天——CORS跨域资源共享及jQuery中$.ajax()的使用
第二种解决非同源数据的方案
CORS:Corss orgin resource sharing 即跨域资源共享,它允许浏览器向跨域服务器发送Ajax请求,克服了Ajax只能同源使用的限制。
具体使用:需要在服务器端进行配置,客户端无需配置
服务器端代码如下:

1
2
3
4
5
6
7
app.get('/cross',(req,res) => {
// 1.允许哪些客户端访问我
// * 代表所有的客户端访问我
res.header('Access-Control-Allow-Origin','*');
// 2.允许客户端使用哪些方法访问我
res.header('Access-Control-Allow-Method','get,post');
});

第三种解决非同源数据的方案
同源政策是浏览器给予Ajax技术的限制,服务器端是不存在同源政策限制的,因此我们可以先将客户端的请求发送给自己的服务器端,由自己的服务器端与非同源的服务器端进行访问,拿到数据后,再将数据返回给自己的客户端,相当于在自己的客户端与非同源服务器端之间增加了一座桥梁,就是自己的服务器端。
本方法的提出要先了解Cookie的作用,最初的时候,客户端与服务器端使用HTTP协议进行连接,它们之间的连接是无状态的,即没有记录功能,相当于两端都互不认识,Cookie提出后,相当于一张它们的名片,使得客户端与服务器端彼此认识,这时他们彼此就可以互相访问了。
withCredentials属性
在使用Ajax技术发送跨域请求时,默认情况下不会再请求种携带Cookie信息,所以它们彼此的访问是不被允许的。此时,只需修改本属性的值为true即可,需要在客户端与服务器端都进行配置。
服务器端代码添加以下一行:
// 允许客户端发送跨域请求时携带Cookie信息
res.header('Access-Control-Allow-Credentials',true);
客户端代码添加以下一行:
// 当发送跨域请求时携带Cookie信息 默认值为false
xhr.withCredentials = true;


jQuery中 $.ajax() 的使用

jQuery中将Ajax已经封装好了,使用即可,其中有几个属性注意一下使用方式即可

  1. ContentType: 表示发送数据的类型,默认为 application/x-www-form-urlencoded

  2. data: 表示传入的数据,可以传对象,也可以传字符串,最终它都会把它转换为字符串处理

  3. beforeSend:function(){} 表示在请求发送之前会调用此函数,可以放入等待缓冲的图片,提高用户体验

  4. dataType:’jsonp’ jQuery中的$.ajax()方法还可以发送jsonp请求,这点是比较方便的,有两个可选参数:

    1. josnp:'cb'表示修改callback参数的名称 即向服务器端传递函数名字的参数名称
    2. jsonpCallback:'fnName' 表示指定函数名称,此函数就代替了success那个参数的作用

    serialize方法
    在使用$.ajax()方法中,如果表单中的元素有很多,我们也需要自己拼接参数的格式,本方法与之前的FormData函数类似,不过FromData存在与HTML5中,有兼容性问题,所以在jQuery中,有此方法来替代
    作用:将表单中的数据自动拼接成字符串类型的参数,有且只有这项功能
    很多时候我们需要使用到字符串里某一些的值,这时我们将它转换为对象类型就很方便,但是jQuery中并没有相关的方法,所以这里我们把它封装好了,代码及使用场景如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form id="form">
<input type="text" name="username">
<input type="password" name="psw">
<input type="submit" value="提交">
</form>
<script src="./jquery-3.4.1.min.js"></script>
<script>
$('#form').on('submit',function(){
// var result = $('#form').serialize();
// serializeObject(result);
serializeObject($(this));
return false;
})
// 将表单中用户输入的内容转换为对象类型
function serializeObject(obj){
// 处理结果对象
var Object = {};
// 将结果拼接成数组类型
var result = obj.serializeArray();
// 循环数组 将数组转换为对象类型
$.each(result,function(index,value){
Object[value.name] = value.value;
})
// 将处理的结果返回到函数外部
return Object;
}
</script>
</body>
</html>

全局事件

只要页面中有Ajax请求被发送,对应的全局事件就会被触发

  • .ajaxStart() //当请求开始发送时触发
  • .ajaxComplete() //当请求完成时触发
    注意 以上两个事件均需要绑定在 document 上
1
2
3
$(document).on('ajaxStart',function(){

})

以上两个全局事件可以结合NProgrss进度条插件(在页面的最上端有一个进度条可可以展示当前请求状态),提升用户体验。


RESTful风格的API
概述:它是一套关于设计请求的规范

PUT和DELETE在传统表单中不支持,只有Ajax中支持,具体的要结合相关案例


XML基础
XML:extensible markup language 可扩展标记语言,作用是传输和存储数据
XML DOM:即XML文档对象模型,是W3C组织定义地一套操作XML文档对象的API,浏览器会将XML文档解析成文档对象模型
注:此类型的返回数据已经很少用了,了解即可。