AJAX学习内容

200 阅读7分钟

AJAX概述

这个技术涉及发送服务器请求额外数据而不刷新页面,从而实现更好的用户体验,改变了WEB诞生以来就一直延续的传统单击等待的模式。

XHR为发送服务器请求和获取响应提供了合理的接口,可以实现从服务器获取额外数据,意味着用户点击不用页面刷新也可以获取数据。

已经过时。

客户端请求的基本操作:

使用XHR对象首先调用open()方法,接受三个参数:请求类型、请求URL、表示请求是否异步的布尔值(true为异步)。调用open()不会实际发送请求,只是为发送请求做准备。

注意:只能访问同源URL

send()方法接受一个参数,是作为请求体发送的数据。如果不需要请求体,则必须传null,因为这个参数在某些浏览器中是必需的。调用send()之后,请求就会发送到服务器。

收到响应后,XHR对象的以下属性会被填充上数据:

  • responseText:作为响应体返回的文本
  • responseXML:如果相应内容类型是'text/xml'或'application/xml',那就是包含响应数据的XML DOM文档。
  • status:相应的HTTP状态
  • statusText:相应的HTTP状态描述

同步请求实例:

const xhr = new XMLHttpRequest()//初始化
xhr.open('GET','http://localhost:3000/server',false)//设置url和请求类型
xhr.send(null)//发送请求
if((xhr.status >=200 && xhr.status < 300) || xhr.status == 304){
    alert(xhr.responseText)
} else {
    alert('request was unsussessful: ' + xhr.status)
}

多数情况下请使用异步请求,这样可以不阻塞JS代码的执行。

XHR对象有一个属性:readyState,表示当前处在请求/响应的哪个阶段。 从 0 到 4 发生变化:

  • 0(Uninitialized): 请求未初始化,尚未调用open()方法
  • 1(Open): 已调用open(),未调用send()方法
  • 2(Sent): 请求已发送,尚未收到响应
  • 3(Receiving): 已经收到部分响应
  • 4(Complete): 已经收到所有响应

每次readyState属性改变,都会触发readystatechange事件。为保证跨浏览器兼容,onreadystatechange事件处理程序应该在调用open()之前赋值。

异步请求例子:

let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if((xhr.status >= 200 && xhr.status < 300) || xhr.status ==304){
            alert(xhr.responseText)
        } else {
            alert('error')
        }
    }
}
xhr.open('get','example.php',true) //异步请求
xhr.send(null)

HTTP头部

默认情况下,XHR请求会发送以下头部字段:

  • Accept:浏览器可以处理的内容类型
  • Accept-Charset:浏览器可以显示的字符集
  • Accept-Encoding:浏览器可以处理的压缩编码类型
  • Accept-Language:浏览器使用的语言
  • Connection:浏览器与服务器的连接类型
  • Cookie:页面中设置的Cookie
  • Host:发送请求的页面所在的域
  • Referer:发送请求的页面的URI
  • User-Agent:浏览器的用户代理字符串

如果需要发送额外的请求头部,可是使用setRequestHeader()方法,接受两个参数,头部字段的名称和值,必须在open()之后,send()之前调用。

可以使用getResponseHeader()方法从XHR对象获取响应头部,传入参数为要获取的头部的名称。

使用getAllResponseHeaders()方法取得所有响应头部,这个方法返回包含所有响应头部的字符串

设置请求参数:

xhr.open('GET','http:localhost:3000/server?a=100&b=200&c=300',true)

发送post请求:

function submitData(){
    let xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function(){
        if(xhr.readystate == 4){
            if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                alert(xhr.responseText)
            } else {
                alert('request was unseccessful')
            }
        }
    }
    xhr.open('post', 'postexample.php', true)
    xhr.setRequestHeader('Connect-Type','application/x-www-form-urlencoded')
    let form = document.getElementById('user-info')
    xhr.send(serialize(form)) // 需要序列化
}

服务端也需要有post请求的设置

app.post('/server',(request, response)=>{
    //设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*')
    
    response.send('hello express post')
})

post设置请求体:

在send()中设置参数: xhr.send('a=100&b=100&c=100');

查看:

image.png

可以是任意格式、任意内容: xhr.send('a:100&b:100&c:100'); 但是需要服务端有对应的处理。

设置请求头信息

在xhr.open()后调用setRequestHeader('','')方法:

xhr.setRequestHeader('Content-Type','application/x-www-formurlencoded');

效果:

image.png

可以添加自定义的请求头,但需要服务器代码进行一些设置。

XMLHttpRequest Level 2

对XHR对象的进一步发展:

1.formData类型,便于表单序列化

let data = new FormData()
data.append('name', 'Nicholas')
// 也可以直接给FormData构造函数传入一个表单元素
let data2 = new FormData(document.forms[0])
let form = document.getElementById('user-info') 
xhr.send(new FormData(form))

2.超时

IE8给XHR增加了一个timeout属性,发动请求后等待一段时间,如果相应不成功就中断请求,调用ontimeout事件处理程序。

...
xhr.open('get', 'timeout.php', 'true')
xhr.timeout = 1000
xhr.ontimeout = function(){
    alert('request did not return in a second')
}
xhr.sendnull)

服务端响应json数据

服务端能传输的只有字符串和buffer,所以需要用JSON.stringify()方法转换成字符串

app.all('/json-server',(request, response)=>{
    //设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');
    const data = {
        name:'hhh'
    };
    //继续字符串转换
    let str = JSON.stringify(data);
    response.send(str)
})

前端收到的数据是字符串形式,有两种方法转换,手动转换和自动转换。

手动转换:

const data = JSON.parse(xhr.response)
console.log(data);
result.innerHTML = data.name

自动转换: 初始换xhr之后设置响应体数据类型:

xhr.responseType = 'json'

这样直接拿到的response就是转换过的数据

result.innerHTML = xhr.response.name

IE缓存问题

给URL添加一个唯一的ID

xhr.open('GET','http://127.0.0.1:8000/ie?t='+Date.now())
xhr.open('GET','http://127.0.0.1:8000/ie?t='+Math.random())

请求超时、网络故障、取消请求

请求超时设置:

xhr.timeout = 2000;//设置超时时间
xhr.onTimeout = function(){//绑定超时回调函数
alert('请求超时')
}

网络故障设置:

xhr.onerror = function(){//网络故障回调
...
}

取消网络请求:

xhr.abort();

请求重复发送的问题

我对问题的理解:如果用户在短时间内重复多次发送网络请求,会对 服务器造成负担,通过解决这个问题,可是使同一时刻只有一个网络请求在向服务器发送请求。(个人理解) 使用标识变量

let x = null;
let flag = false;
btn.onclick = function(){
    if(flag) x.abort();//如果正在发送就取消上一个请求,创建一个新的请求
    x = new XMLHttpRequest();
    flag = true;
    ...
    x.onreadystatechange = function(){
        if(x.readyState === 4){
            flag = false;//响应后修改标识变量
            ...
        }
    }
}

JQuery中的AJAX

GET方法

$.get(url,[data],[callback],[type]) data:携带参数 callback:载入成功的回调函数 type:设置返回内容格式,'xml','html','json','text','script' 例:

<script>
    $.(button).eq(0).click(function(){
        $.get('http://127.0.0.1:8000/jqury-server',
        {a:110,b:200},
        function(data){
        console.log(data);
        },
        'json')
    })
</script>

POST方法

$.get(url,[data],[callback],[type]) 例:

$.(button).eq(1).click(function(){
        $.post('http://127.0.0.1:8000/jqury-server',
        {a:110,b:200},
        function(data){
        console.log(data);
        },
        'xml')
    })

通用方法

$.(button).eq(2).click(function(){
        $.ajax({                                  //参数是一个对象
        url:'http://127.0.0.1:8000/delay',
        data:{a:100,b:200},                       //url参数
        type:'GET',                               //请求类型
        dataType:'json',                          //响应体结果类型
        timeout:2000,                             //超时时间
        succecc:function(data){                   //成功回调
            console.log(data);
            }
        error:function(){                         //失败回调
            console.log('ERROR!')
        },
        headers:{                                 //添加头信息
            c:300;
            d:400
        }
        })
    })

自定义程度强,参数内的对象有很多可设置的内容。

Axios发送AJAX请求

get请求:axios.get(url[,config])第二个参数是一个设置参数的对象

axios.defaults.baseURL = 'http:127.0.0.1:8000';     //设置默认路径
btns[0].onclick = function(){
    axios.get('/axios/server',{             //第二个参数是一个对象
        params:{                            //参数
            id:100,
            id2:111
        },
        headers:{                           //添加的请求头信息
            name:'aaa',
            age:99
        }
    }).then(value => {                      //基于promise设置回调函数
        console.log(value);
    });        
    )
}

post请求:axios.post(url[,data[,config]])第二个参数是请求体,第三个参数是其他配置

btns[1].onclick = function(){
    axios.post('/axios/server',{           //第二个参数---请求体-一个对象
            usrname:'admin',
            password:'hhh'
        },
        {   params:{                       //第三个参数---其他配置-一个对象
            id:200,
            id2:11
            }
            headers:{                       //请求头参数
                name:'aaa',
                age:99
            }
        }).then(value => {                     
        console.log(value);
        });        
        )
}

axios函数发送请求(通用方法)

btn.onclick = function(){
axios({
    method:'post',                    //请求方法
    url:'http://bit.ly/2mTM3nY',
    responseType:'stream',
    params:{                          //url参数
        id:100,
        id2:111
    }
    headers:{                          //请求头
        a:100,
        b:200
    }
    data:{                             //请求体参数
        username:'admin',
        password:'hhh'
    }
})
  .then({
      response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
  });//这个是github中的函数,下面是尚硅谷视频中的
  //或者是下面的函数
  .then(response => {                       //返回的数据是已经解析过的 
      console.log(response);
      console.log(response.status);         //响应状态码
      console.log(response.statusText);     //响应状态字符串
      console.log(response.headers);        //响应头信息
      console.log(response.data);           //响应体
  })
};

使用全局函数fetch

fetch(input[,init]);

input:url

init:可选,一个配置项对象,包括所有请求的设置。可选参数:

  • method:请求方法,'GET','POST'
  • headers:请求头信息
  • body:请求体
btn.onclick = function(){
    fetch('http://127.0.0.1:8000/fetch-server?vip=10',{  //url参数直接写在url中
        method:'POST',
        headers:{
            name:'hhh'
        },
        body:'username=admin&password=admin'    //POST请求设置请求体
    }).then(response => {
        return response.text();      //json类型数据使用response.json()
    }).then(response => {
        console.log(response);
    })
}

同源策略

一种安全策略。

同源:协议、域名、端口号 完全相同

如何解决跨域

JSONP(只支持GET请求)

通过script 标签的跨域能力实现跨域发送请求,请求的路径作为script的src属性。

原理:在网页中哟一些标签天生具有跨域能力,比如:img,link,iframe,script。

使用jsonp实现跨域步骤:

  1. 动态创建一个script 标签
  2. 设置script标签的src属性
  3. 将script标签插入文档中

功能实现部分相关代码

input.onblur = () => {
            let username = this.value;
            //向服务器发请求
            //创建一个script标签
            const script = document.createElement('script');
            //设置script标签的src属性
            script.src = 'http:127.0.0.1:8000/check-username';
            //将script插入文档中
            document.body.appendChild(script)
        }

服务器发送的响应内容应该是js代码段,而不是简单的数据或字符串

app.all('/check-username',(request,response) => {
    const data = {
        exist:'1',
        msg:'用户名已存在'
    };
    //将数据转换成字符串
    let str = JSON.stringify(data);
    //返回结果
    response.end(`handle(${str})`);
})

上面部分是服务器端的代码,代码响应部分是

response.end(`handle(${str})`);

handle函数在html页面中定义:

function handle(data){
            input.style.border = 'solid 10px #456';
            p.innerHTML = data.msg;
        }

jquery发送jsonp请求

发送请求:

  1. url中一定要有callback参数。
  2. jquery自动注册一个callback函数,服务器端也能接收,用request.query.callback获取。
  3. 将callback的值作为函数调用的函数拼接字符串(个人理解:不再需要在使用html中handle()函数对返回结果处理,可以直接得到返回结果——可以只有数据)
$('button').eq(0).click(function(){
    $.getJSON('http:127.0.0.1:8000/jquery-jsonp-server?callback=?',
    function(data){
        $('#result').html(`
            名字:${data.name},<br/>
            城市:${data.city}
        `)
    })
})

服务器响应:

app.all('/jquery-jsonp-server',(request,response) => {
    const data = {
        name:'hhh',
        city:['beijing','shanghai']
    };
    //将数据转换成字符串
    let str = JSON.stringify(data);
    //接受callback参数
    let cb = request.query.callback
    //返回结果;
    response.end(`${cb}(${str})`);
})

设置CORS响应实现跨域

CORS:跨域资源共享

在服务器端对响应的请求头进行设置

必须字段:
Access-Control-Allow-Origin:表示服务端允许的请求源,*标识任何外域,多个源 , 分隔

可选字段
Access-Control-Allow-Credentials:false 表示是否允许发送Cookie,设置为true
                                 同时,ajax请求设置withCredentials = true,浏览
                                 器的cookie就能发送到服务端

Access-Control-Expose-Headers:调用getResponseHeader()方法时候,能从header中获
                               取的参数

更多关于CORS的知识:阮一峰——跨域资源共享 CORS 详解 www.ruanyifeng.com/blog/2016/0…