Ajax——运行原理和实现、异步编程、函数封装

一、Ajax基础

传统网站中存在的问题

  • 网速慢的情况下,页面加载时间长,用户只能等待
  • 表单提交后,如果一项内容不合格,需要重新填写所有表单内容
  • 页面跳转,重新加载页面,造成资源浪费,增加用户等待时间

1.1 Ajax概述

他是浏览器提供的一套方法,可以实现页面无刷新更新数据,在不刷新页面的情况下和服务器进行交互,

主要目的:提高用户浏览器网站应用的体验

1.2 Ajax的应用场景

  • 1. 页面上拉加载更多数据
  • 2. 列表数据无刷新分页
  • 3. 表单项离开焦点数据验证
  • 4. 搜索框提示文字下拉列表

1.3 Ajax的运行环境

Ajax技术需要运行在网站环境中才能生效,也就是说要能以localhost域名的方式打开页面,不能直接双击HTML文件打开

 


 

二、Ajax运行原理及实现

2.1 运行原理:

实现步骤:

        //1.创建Ajax对象
        var xhr = new XMLHttpRequest();
        //2. 告诉Ajax对象要向哪发送请求,以什么方式发送请求
        //1)请求方式  2)请求地址(其实就是服务器短的路由地址)
        xhr.open('get', 'http://localhost:3000/first');
        //3.发送请求
        xhr.send();
        //4.获取服务器端响应到客户端的数据
        // 由于服务器返回数据的时间是不确定的,我们需要绑定一个onload事件,当xhr对象接受到服务器返回的数据之后,自动触发onload事件
        xhr.onload = function(){
            // xhr对象下面的responseText存储的就是响应的数据
            console.log(xhr.responseText)
        }

  

2.2 服务器端响应的数据格式:

在真实项目中,服务器端大多数情况下会以JSON对象作为响应数据的格式,当客户端拿到响应数据时,要把JSON数据和HTML字符串进行拼接,然后将拼接的结果展示在页面中

在http 请求和响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转换为对象字符串进行传输

JSON.parse(参数) //将JSON字符串转换为JSON对象,JSON是window对象下面的一个属性

 

服务器端返回的json格式的数据到了客户端这边就变成了json格式的字符串,

  • 需要先转换为json对象
  • 然后和HTML拼接
  • 再使用DOM方法放到页面中即可
var responseText = JSON.parse(xhr.responseText)  //将JSON字符串转换为JSON对象
var str = '<h2>'+ responseText.name +'</h2>';   // 和HTML拼接
document.body.innerHTML = str                // 使用DOM方法放到页面中

 2.3 请求参数传递

  • GET请求方式
xhr.open('get', 'http://www.example.com?'+params);
//获取用户在文本框中输入的值
var nameValue = username.value;
var ageValue = age.value;

// 要拼接成 username=123&age=456
var params = 'username='+ nameValue +'&age=' +ageValue;

// 配置Ajax对象
xhr.open('get', 'http://localhost:3000/get?'+ params);
  • POST请求方式

post请求方式必须设置请求报文

报文:在http请求和响应的过程中传递的数据块就叫做报文,报文包括报文头和报文体,这些数据和信息要遵守规定好的格式

报文头:存储一些键值对信息,可以理解为客户端向服务端说的一些话

报文体:主要存储一些信息,比如post请求参数就是存储在报文体中

报文头和报文体在请求的过程中一起发送到服务器

1.post请求参数时,拼接出来的参数一定要放在send方法中, 然后就相当于把请求参数放到了请求体中

2.必须明确请求参数的格式类型

//获取用户在文本框中输入的值
var nameValue = username.value;
var ageValue = age.value;

// 要拼接成 username=123&age=456
var params = 'username='+ nameValue +'&age=' +ageValue;

// 配置Ajax对象
xhr.open('post', 'http://localhost:3000/post');

// 设置请求参数的格式类型 xhr.setRequestHeader(
'Content-type','application/x-www-form-urlencoded'); // 发送请求 xhr.send(params);

 

 

在服务器端的app.js需要添加以下代码

// post请求需要的第三方包,从而可以使用req.body
const bodyParser = require('body-parser');

// 解析 application/x-www-form-urlencoded 这种格式的请求参数
app.use(bodyParser.urlencoded());

app.post('/post', (req, res) =>{
    // 通过req.body 获取客户端传递给服务器端的post请求参数
    res.send(req.body);
})

 

2.4 传递json格式的请求参数

请求参数的格式有两种

1. application/x-www-form-urlencoded

name=zhangsan&age=26&sex=男

 

2. application/json

{name: 'zhangsan', age:'20', sex:'男'}

 

 

当传递json格式类型的请求参数的时候

1. 在请求头中指定 Content-Type 属性的值为 application/json,告诉服务器端当前请求参数的格式是json

xhr.setRequestHeader('Content-Type','application/json');

 

2. json格式类型的参数必须使用POST请求方式,get请求不能提交json对象数据格式,传统网站表单提交也不支持json对象数据格式

3.将json对象转换为json字符串进行传递

JSON.stringify({name:'lisa', age:50})

 

4. 在服务器端解析请求参数的格式需要引入第三方包,并且针对不同的格式设置不同的解析方式

// post请求需要的第三方包
const bodyParser = require('body-parser');
// 解析 application/x-www-form-urlencoded 这种格式的请求参数
app.use(bodyParser.urlencoded());
// 解析 application/json 这种格式的请求参数
app.use(bodyParser.json());


// 字符串拼接格式 app.post('/post', (req, res) => { res.send(req.body); }); // json格式 app.post('/json', (req, res) => { res.send(req.body); });

 

2.5 获取服务器端的响应

Ajax状态码:在创建Ajax对象、配置Ajax对象、发送请求一级接受完服务器端响应数据,这个过程中的每一个步骤都会对应一个数值,这个数值就是Ajax状态码

0:请求未初始化(还没有调用open())

1:请求已经建立,但是还没有发送(还没有调用send())

2:请求已经发送

3:请求正在处理中,通常响应中已经有部分数据可以用了

4:响应已经完成,可以获取并使用服务器的响应了

xhr.readyState // 获取Ajax状态码

 

 

两种获取服务器端响应方式的区别

 

 

 推荐使用onload,除非需要兼容IE低版本

 

2.6 Ajax错误处理 

 

Ajax状态码:表示Ajax请求过程状态 Ajax对象返回的

http状态码:表示请求的处理结果,是服务器端返回的

 

2.7 低版本IE浏览器的缓存问题

 

问题:在低版本的IE浏览器中,Ajax请求有严重的缓存问题,即在请求地址不发生变化的情况下,只有第一次请求会真正的发送到服务器端,后续的请求都会从浏览器的缓存中获取结果,即使服务器端的数据更新了,客户端依然拿到的是缓存中的旧数据

解决方案:在请求地址的后面加请求参数,保证每一次请求中的请求参数的值不相同 

xhr.open('get', 'http://www.example.com?t=' + Math.random());

 

三、Ajax异步编程

3.1 

同步:代码逐行完成,一行代码完成之后才执行下一行代码

异步:一个人事情做了一二班,转而去做其他事情,当其他事情做完之后,再回过头来继续做之前未完成的事情

异步代码虽然需要花时间去执行,但程序不会等待异步代码执行完成之后再继续执行后续代码,而是直接执行后续代码,当后续代码执行完成后再回头看异步代码是否返回结果,如果已有返回结果,再调用事先准备好的回调函数处理异步代码执行结果

事件处理函数就是一个回调函数

xhr.onload = function(){
            console.log(2)
            // xhr对象下面的responseText存储的就是响应的数据
            console.log(xhr.responseText)
        }
console.log(1)
// 会先输出1,在输出2

 

3.2 Ajax封装

问题:发送一次请求代码过多,发送多次请求代码冗余且重复

解决方案:将请求的代码封装到函数中,发送请求时调用函数即可

(1)最基本的封装

function ajax(options){
        //创建ajax对象
        var xhr = new XMLHttpRequest();
        //配置ajax对象
        xhr.open(options.type, options.url);
        // 发送请求
        xhr.send();
        // 获取响应结果,监听xhr对象下面的onload事件
// 当xhr对象接受完响应数据后被触发 xhr.onload = function(){
// 对于下面返回的服务器结果,我们想把这个数据放到函数体的外面,让用户决定它的使用方式,在下面传递的对象中创建了一个success函数
// 在这里调用success函数,并且把请求的结果传递到函数中作为形参 options.success(xhr.responseText); } }
// 上面封装了一个ajax函数,下面调用这个函数,并且传递了一个实参,实参中包含了请求方式和请求地址 ajax({ // type代表请求方式 type: 'get', // 请求地址 url: 'http://localhost:3000/first', // success这个函数在请求成功之后被调用,请求成功也就是执行onload事件的时候就代表请求成功,也就是在onload事件处理函数被触发就代表请求成功, // 下面这个函数是一个定义,data是一个形参,接受服务器响应结果 success: function(data){ console.log('这里是success函数'+data); } })

 

(2)发送请求时需要传递参数

要考虑的问题:

  • 请求参数位置的问题:将请求参数传递到ajax函数内部,在函数内部根据请求方式的不同将请求参数放置在不同的位置

          get  放在请求地址的后面;post  放在send方法中

  • 请求参数格式的问题:         
application/x-www-form-urlencoded
       参数名称=参数值&参数名称=参数值
       name=zhangsan&age=20
application/json
       {name: 'zhangsan', age: 20}

 

1. 传递对象数据类型对于函数的调用者更加友好

2. 在函数内部对象数据类型转换为字符串数据类型更加方便

<script type="text/javascript">
    function ajax(options){

        // 存储的是默认值,在调用这个函数的时候,我们不希望每次都传递大量参数,因此设置一个默认值
        // 如果传递了这个参数,就覆盖掉默认值,否则使用默认值
        var defaults = {
            type:'get',
            url:'',
            data:{},
            header:{
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            success:function(){},
            error:function(){}
        };

        // 使用options 对象中的属性覆盖defaults 对象中的属性
        Object.assign(defaults, options);

        //创建ajax对象
        var xhr = new XMLHttpRequest();
        // 拼接请求参数的变量
        var params = '';
        // 循环用户传递进来的对象格式参数
        for(var attr in defaults.data){
            params += attr + '=' + defaults.data[attr] + '&';
        }
        // 将参数最后面的 & 截取掉,并将结果重新赋值给params
        params = params.substr(0,params.length-1);

        // 判断请求方式
        if(defaults.type == 'get'){
            defaults.url = defaults.url + '?' + params;
        }

        //配置ajax对象
        xhr.open(defaults.type, defaults.url);
        // 如果请求方式为post
        if(defaults.type == 'post'){
            // 用户希望的向服务器段传递的请求参数的类型
            var contentType = defaults.header['Content-Type']
            // post请求需要明确 设置请求方式格式的类型
            xhr.setRequestHeader('Content-Type', contentType);

            // 如果传递的是json类型的参数,只能使用post请求方式
            if(contentType == 'application/json'){
                // 将json对象转换为json字符串进行传递
                xhr.send(JSON.stringify(defaults.data))
            }else{
                // 向服务器传递普通类型的请求参数
                xhr.send(params);
            }
        }else{
            // 如果请求方式是get
            xhr.send();
        }
        // 获取响应结果,监听xhr对象下面的onload事件
        xhr.onload = function(){
// 使用ajax技术向服务器发送请求的时候,一般都会返回json类型的数据,但是客户端拿到的是json字符串
// 要想使用这个数据就需要把字符串转换为json对象,我们想把这个过程封装到函数当中,这样函数的调用者就不需要关心这个细节了,拿到的就直接是json对象
// 但是在函数内部定义这个函数的时候,我们不知道服务器端返回的数据是什么类型的,通过xhr.getResponseHeader获取返回的数据,Content-Type就代表返回的数据类型
            // xhr.getResponseHeader()
            // 获取响应头当中的数据
            var contentType = xhr.getResponseHeader('Content-Type');

            // 服务器端返回的数据
            var responseText = xhr.responseText

            // 如果响应类型中包含application/json
            if(contentType.includes('application/json')){
                // 将json字符串转换为json对象
                responseText = JSON.parse(responseText)
            }

            // 当http状态码等于200的时候
            if(xhr.status == 200){
                // 请求成功,调用处理成功情况的函数
                // xhr对象中包含很多信息,因此可以吧xhr也传递出去
                defaults.success(responseText, xhr);
            }else{
                // 请求失败 调用处理失败情况的函数
                defaults.error(responseText, xhr);
            }
            
        }

    }

    // 上面封装了一个ajax函数,下面调用这个函数,并且传递了一个实参,实参中包含了请求方式和请求地址
    ajax({
        // type代表请求方式
        type: 'get',
        // 请求地址
        url: 'http://localhost:3000/responseData',
        data: {
            name: 'zhangsan',
            age: 20
        },
        header: {
            'Content-Type': 'application/json'
        },
        // success这个函数在请求成功之后被调用,请求成功也就是执行onload事件的时候就代表请求成功,也就是在onload中调用success函数
        // 下面这个函数是一个定义,data是一个形参
        success: function(data){
            console.log('这里是success函数');
            console.log(data)
        },
        error: function(data, xhr){
            console.log('这里是error函数,'+ data);
            console.log(xhr)
        }
    })
    </script>

 

posted @ 2020-04-28 10:17  ccv2  阅读(397)  评论(0编辑  收藏  举报