Ajax基本使用与封装

1,334 阅读8分钟

Ajax基本使用与封装.png

一、原生AJAX

1.简介

AJAX 全称为Asynchronous Javascript And XML,就是异步的 JS 和 XML。 通过AJAX可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据

2.AJAX的特点

2.1AJAX的优点

1)可以无需刷新页面而与服务器端进行通信。

2)允许你根据用户事件来更新部分页面内容。

2.2AJAX的缺点

1)没有浏览历史,不能回退

2)存在跨域问题

3)SEO不友好

3.使用原生js发送ajax_get(重点!)

基本结构:(以下代码必须记住!!!)

//1.实例化一个XMLHttpRequest对象
let xhr = new XMLHttpRequest()

//2.绑定一个名为onreadystatechange事件监听
xhr.onreadystatechange = function () {
  if(xhr.readyState === 4 && xhr.status === 200){
    //如果进入此判断,意味着:请求成功了,且数据已经回来了
    console.log(xhr.response)
  }
}

//3.调用open方法---------用什么方法发?给谁发?带着什么过去?
xhr.open('get','http://localhost:3000/ajax_get?name=kobe&age=18')

//4.调用send方法---------发送请求
xhr.send()

完整代码:

//使用原生js发送ajax_get
/*
* 1.实例化一个XMLHttpRequest对象,名为:xhr---------找来一个帮你发请求的人。
* 2.绑定一个名为onreadystatechange事件监听------和发请求的人约定好--成功了做什么,失败了做什么。
* 3.调用open方法---------用什么方法发?给谁发?带着什么过去?
* 4.调用send方法---------发送请求
* */

//1.实例化一个XMLHttpRequest对象
let xhr = new XMLHttpRequest()

//2.绑定一个名为onreadystatechange事件监听
xhr.onreadystatechange = function () {
  //xhr对象本身有5种状态:xhr“诞生”的那一刻就是0状态
  /*
  * 0:xhr对象在实例化出来的那一刻,就已经是0状态,代表着xhr是初始化状态。
  * 1:send方法还没有被调用,即:请求没有发出去,此时依然可以修改请求头。
  * 2:send方法被调用了,即:请求已经发出去了,此时已经不可以再修改请求头。
  * 3:已经回来一部分数据了,如果是比较小的数据,会在此阶段一次性接收完毕,较大数据,有待于进一步接收。
  * 4:数据完美的回来了。
  * */

  //我们几乎不会在0状态里做任何事。即:如下判断0状态,永远进不去。
  /*if(xhr.readyState === 0){
    console.log('我是一个刚出生的娃娃')
  }*/
  //1:send方法还没有被调用,即:请求没有发出去,此时依然可以修改请求头。
  /*if(xhr.readyState === 1){
    console.log('我是1状态')
    xhr.setRequestHeader('demo','123')
  }*/
  //2:send方法被调用了,即:请求已经发出去了,此时已经不可以再修改请求头。
  /*if(xhr.readyState === 2){
    console.log('我是2状态')
    xhr.setRequestHeader('demo','123')
  }*/
  //3.已经回来一部分数据了,如果是比较小的数据,会在此阶段一次性接收完毕,较大数据,有待于进一步接收。
  /*if(xhr.readyState === 3){
    console.log(xhr.response)
  }*/
  //4:数据完美的回来了。
  if(xhr.readyState === 4 && xhr.status === 200){
    //如果进入此判断,意味着:请求成功了,且数据已经回来了
    console.log(xhr.response)
    let demo = document.getElementById('demo')
    demo.innerHTML = xhr.response
  }
}

//3.调用open方法---------用什么方法发?给谁发?带着什么过去?
xhr.open('get','http://localhost:3000/ajax_get?name=kobe&age=18&t='+Date.now())

//4.调用send方法---------发送请求
xhr.send()

4.使用原生js发送ajax_post(重点!)

//1.实例化一个XMLHttpRequest对象
let xhr = new XMLHttpRequest()

//2.绑定一个名为onreadystatechange事件监听
xhr.onreadystatechange = function () {
  if(xhr.readyState === 4 && xhr.status === 200){
    console.log(xhr.response)
    let demo = document.getElementById('demo')
    demo.innerHTML = xhr.response
  }
}

//3.调用open方法---------用什么方法发?给谁发?带着什么过去?
xhr.open('post','http://localhost:3000/ajax_post')

//特别注意:此处是设置post请求所特有的请求头,若不设置,服务器无法获取参数
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')

//4.调用send方法---------发送请求
xhr.send('name=kobe&age=18')

5.解决IE缓存问题

问题: 在一些浏览器中(IE),由于缓存机制的存在,ajax只会发送第一次请求,剩余多次请求不会在发送给服务器而是直接加载缓存中的数据。

解决方式: 浏览器的缓存是根据url地址来记录的,所以我们只需要修改url地址即可避免缓存问题 xhr.open("get","/testAJAX?t="+Date.now());

6.取消上一次请求(实现类似防抖)

xhr.abort():

1.如果来得及,半路取消,请求根本没有到达服务器。

2.如果来不及,拒之门外,请求已经到达了服务器,且服务器已经给出响应。

3.也存在根本不起作用的情况。

完整示例:

//node服务器代码:
//返回验证码的路由,每当有人请求该路由,该路由就会返回一个1000 - 9999的随机数
app.get('/get_code',function (req,res) {
  console.log('有人找我要验证码了')
  setTimeout(()=>{
    let authCode = Math.floor(Math.random()*8999 + 1000)
    console.log(authCode)
    res.send(authCode.toString())
  },4000)
})

//客户端代码:
let btn = document.getElementById('btn')
let lastXhr

btn.onclick = function () {
    if(lastXhr){
        lastXhr.abort() //用于“取消”本次请求
    }
    lastXhr = getAuthCode()
}

//专门用于请求验证码的函数
function getAuthCode() {
    let xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function () {
        if(xhr.readyState === 4 && xhr.status === 200){
            //如果进入此判断,意味着:请求成功了,且数据已经回来了
            console.log(xhr.response)
        }
    }
    xhr.open('get','http://localhost:3000/get_code')
    xhr.send()
    //xhr.abort() //用于“取消”本次请求
    return xhr
}

7.XHR的理解和使用

7.1理解

1.使用XMLHttpRequest (XHR)对象可以与服务器交互, 也就是发送ajax请求

2.前端可以获取到数据,而无需让整个的页面刷新。

3.这使得Web页面可以只更新页面的局部,而不影响用户的操作。

7.2区别一般http请求与ajax请求

1.ajax请求是一种特别的http请求

2.对服务器端来说, 没有任何区别, 区别在浏览器端

3.浏览器端发请求:只有XHR 或 fetch发出的才是 ajax请求, 其它所有的都是 非ajax请求

4.浏览器端接收到响应

(1) 一般请求:浏览器一般会直接显示响应体数据, 也就是我们常说的刷新/跳转页面

(2) ajax请求:浏览器不会对界面进行任何更新操作, 只是调用监听回调函数并传入响应相关数据

7.3API

1.XMLHttpRequest(): 创建XHR对象的构造函数

2.status: 响应状态码值, 比如:200, 404

3.statusText: 响应状态文本

4.readyState: 标识请求状态的只读属性

0:  初始

1:  open()之后

2:  send()之后

3:  请求中

4:  请求完成

5.onreadystatechange: 绑定readyState改变的监听

6.responseType: 指定响应数据类型, 如果是'json', 得到响应后自动解析响应体数据

7.response: 响应体数据, 类型取决于responseType的指定

8.timeout: 指定请求超时时间, 默认为0代表没有限制

9.ontimeout: 绑定超时的监听

10.onerror: 绑定请求网络错误的监听

11.open(): 初始化一个请求, 参数为: (method, url[, async])

12.send(data): 发送请求

13.abort(): 中断请求

14.getResponseHeader(name): 获取指定名称的响应头值

15.getAllResponseHeaders(): 获取所有响应头组成的字符串

16.setRequestHeader(name, value): 设置请求头

二、jQuery封装的AJAX

实例:

let btn1 = $('#btn1')
let btn2 = $('#btn2')
  
btn1.click(function () {

    //使用jQuery发送ajax-get请求------完整版写法 --- 2个参数
    /*$.ajax('http://localhost:3000/ajax_get',{
      method:'get', //发送请求的方式
      data:{name:'kobe',age:41}, //发送请求携带的数据
      //成功的回调
      success:function (result) {
        console.log(result)
      },
      //失败的回调
      error:function (err) {
        console.log(err)
      }
    })*/

    //使用jQuery发送ajax-get请求------简写
    $.get('http://localhost:3000/ajax_get',{name:'kobe',age:41},(data)=>{
        console.log(data)
    })
})

btn2.click(function () {
    //使用jQuery发送ajax-post请求------完整版写法 --- 1个参数
    $.ajax({
        url:'http://localhost:3000/ajax_post',
        method:'post', //发送请求的方式
        data:{name:'kobe',age:41}, //发送请求携带的数据
        //成功的回调
        success:function (result) {
            console.log(result)
        },
        //失败的回调
        error:function (err) {
            console.log(err)
        }
    })

    //使用jQuery发送ajax-post请求------简写
    $.post('http://localhost:3000/ajax_post',{name:'kobe',age:41},(data)=>{
        console.log(data)
    })
})

三、同源策略与跨域解决

1.为什么会有跨域这个问题?

原因是浏览器为了安全,而采用的同源策略(Same origin policy)

2.什么是同源策略?

1.同源策略是由Netscape提出的一个著名的安全策略,现在所有支持JavaScript 的浏览器都会使用这个策略。

2.Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

3.所谓同源是指:协议,域名(IP),端口必须要完全相同

即:协议、域名(IP)、端口都相同,才能算是在同一个域里。

3.非同源受到哪些限制?(跨域会存在哪些问题?)(重点!)

1.Cookie不能读取

2.DOM无法获得

3.Ajax请求不能获取数据 (AJAX请求能发送,但不能获取数据,对服务器无影响,对浏览器有影响)

面试题:

为什么form表单不受同源策略的限制,而ajax受同源策略的限制?

同源策略是限制浏览器跨域获取数据,引起安全问题,但浏览器不阻止你像其他域名发出请求。那么form发送请求能够跨域就可以理解,它只是发出请求,并没有得到响应。

4.如何在开发中解决跨域问题:(重点!)

4.1JSONP解决发送请求跨域问题:

1.原理:利用了script标签发请求不受同源策略的限制。所以不会产生跨域问题

2.套路:动态构建script节点,利用节点的src属性,发出get请求,从而绕开ajax引擎

3.弊端:(1) 只能解决get请求跨域的问题 (2) 后端工程师必须配合前端

原生js完整实例:

//前端:
<script type="text/javascript">
  let btn = document.getElementById('btn')
  btn.onclick = function () {
    //1.提前定义好一个等待被调用的函数
    window.peiqi = function(result) {
      console.log(result)
    }
    //2.创建一个script节点
    const scriptNode = document.createElement('script')
    //3.为节点指定src地址,同时指定好回调函数的名字
    scriptNode.src = 'http://localhost:3000/test?callback=peiqi'
    //4.将节点插入页面
    document.body.appendChild(scriptNode)
  }

//后端:
app.get('/test',function (req,res) {
  let {callback} = req.query
  console.log(callback)
  let personArr = [{name:'peiqi',age:12},{name:'suxi',age:16}]
  res.send(`${callback}(${JSON.stringify(personArr)})`)
})

jQuery完整示例:

1.提前定义好一个等待被调用的函数

2.创建一个script节点

3.为节点指定src地址,同时指定好回调函数的名字

4.将节点插入页面

备注:以上所有步骤,都由jquery底层全部完成

$('#btn').click(function () {
    //完整写法
    $.ajax({
      url:'http://localhost:3000/test',
      method:'get',
      dataType:'jsonp', //该属性,控制了上面的4步
      data:{name:'zhangsan',age:18},
      success:function (result) {
        console.log(result)
      },
      error:function (err) {
        console.log(err)
      }
    })
    
    //精简写法
    /*$.getJSON('http://localhost:3000/test?callback=?',{name:'zhangsan',age:18},function (data) {
      console.log(data)
    })*/
})

4.2后台配置cors解决跨域

CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post请求。

CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。

主要是服务器端的设置:( node )

app.get('/test',function (req,res) {
  let personArr = [{name:'peiqi',age:12},{name:'suxi',age:16}]
  res.setHeader('Access-Control-Allow-Origin','http://localhost:63348')
  //res.setHeader('Access-Control-Allow-Origin','*')
  res.send(personArr)
})

4.3使用代理服务器

例如:nginx等

四、封装自己的Ajax - 仿jQuery

function sendAjax(option) {
    //获取配置对象里的属性
    let {url,method,data,success,error} = option
    method = method ? method : 'get' //设置method的默认值

    //实例化xhr
    let xhr = new XMLHttpRequest()
    //绑定监听
    xhr.onreadystatechange = function () {
        if(xhr.readyState !== 4) return
        if(xhr.status >= 200 && xhr.status <= 299){
            if(success) success(xhr.response)
        }else{
            if(error) error('请求出错')
        }
    }

    //整理参数
    let str = ''
    for (let key in data){
        str += `${key}=${data[key]}&`
    }

    //根据请求方式决定如何携带参数
    if(method.toUpperCase() === 'GET'){
        xhr.open(method,url+'?'+str)
        xhr.send()
    }else{
        xhr.open(method,url)
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
        //post要将参数的urlencoded传入send中
        xhr.send(str)
    }
    return xhr
}


//测试自己封装的ajax
sendAjax({
    url:'http://localhost:3000/ajax_post',
    method:'post',
    data:{name:'kobe',age:result},
    success:function (result) {
        console.log(result)
    },
    error:function (err) {
        console.log(err)
    }
})

五、Fetch(关注分离的设计思想)

5.1Fetch简介

Fetch是AJAX的替换方案,基于Promise设计,很好的进行了关注分离,有很大一批人喜欢使用fetch进行项目开发

但是Fetch的缺点也很明显,首先需要明确的是Fetch是一个 low-level(底层)的API,没有帮助你封装好各种各样的功能和实现

比如发送网络请求需要自己来配置Header的Content-Type,不会默认携带cookie等

比如错误处理相对麻烦(只有网络错误才会reject,HTTP状态码404或者500不会被标记为reject)

比如不支持取消一个请求,不能查看一个请求的进度等等

5.2fetch发送请求步骤

1.先判断是否成功连接服务器

2.再判断数据是否获取到

5.3fetch发送网络请求

1)常规发送(未优化)

search = ()=>{
    fetch(`/api1/search/users2?q=${keyWord}`).then(
        response => {
            console.log('联系服务器成功了');
            // console.log(response);
            //返回一个promise
            return response.json()
        },
        error => {
            console.log('联系服务器失败了',error);
            //连接服务器失败,中断promise链(返回pending状态的promise)
            return new Promise(()=>{})
        }
    ).then(
        response => {
            console.log('获取数据成功了',response);
            publish('getUsers', { isLoading: false, users: response.items })
        },
        error => {
            console.log('获取数据失败了',error);
            publish('getUsers', { isLoading: false, err: error.message })
        }
    )
}

2)async / await(优化)

search = async()=>{
    try {
        const response= await fetch(`/api1/search/users2?q=${keyWord}`)
        const data = await response.json()
        console.log(data);
    } catch (error) {
        console.log('请求出错',error);
    }
}