Ajax学习笔记(包含原生以及JQuery,以及跨域解决方案)

Ajax

Ajax概述

它是浏览器提供的一套方法,可以实现页面无刷新更新数据

  • 应用场景

    • 页面上拉加载更多数据
    • 列表数据无刷新分页
    • 表单项离开焦点数据验证
    • 搜索框提示文字下拉列表
  • Ajax的运行环境
    Ajax技术需要运行在网站环境汇总才能生效

Ajax的实现步骤

  1. 创建Ajax对象

    var xhr = new XMLHttpRequest()
    
  2. 告诉Ajax请求地址以及请求方式

    //第一个参数为请求方式,第二个参数是请求地址
    xhr.open('get', 'http://localhost:8000')
    
  3. 发送请求

    xhr.send()
    
  4. 获取服务器端给客户端的响应数据
    监听xhr的onload事件

    xhr.onload = function() {
     	//xhr.responseText是响应给服务器端的数据
        console.log(xhr.responseText)
    }
    

服务器端响应的数据格式

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

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

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

请求参数传递

  • GET请求
//请求参数需要手动拼接字符串
xhr.open('get', 'http://localhost:8000/get?name=zhangsan&age=18')
	```

- POST请求

  ```js
  //设置请求参数格式类型
  //setRequestHeader第一个参数是报文属性名称,第二个参数是报文属性对应的值
  xhr.setRequestHeader('Content-Type','application/x-www-form-urlencode')
  xhr.send('name=zhangsan&age=20')

请求参数格式

  1. application/x-www-form-urlencode

    name=zhangsan&age=18&sex=male
    
  2. application/json

    {name: 'zhangsan', age: 18, sex: 'male'}
    

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

    JSON.stringify() //将json对象转换为json字符串的方法
    

注意:get请求是不能提交json对象数据格式的,传统网站的表单提交也是不支持json对象数据格式的

Ajax运行原理及实现

获取服务器端的响应

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

    0:当xhr被实例化出来,状态就是0,。即初始化状态
    1:请求已经建立但是还没有发送,即:send方法还没有被调用,依然可以修改请求头
    2: 请求已经发出去了,即:send方法已经被调用了,不能再修改请求头,响应首行和响应头已经回来了
    3:数据回来了(但是数据可能不完整,如果数据小,会在此阶段直接接受完毕,数据大有待进一步接收)
    4:响应已经完成,数据完全回来了,可以获取并使用

    xhr.readyState //获取ajax状态码
    
    onreadystatechange//事件监听状态码变化
    

ajax错误处理

  1. 网络畅通,服务器端能接受到请求,服务器端返回的救国不是预期结果
    可以判断服务器端返回的状态码,分别进行处理。
    xhr.status获取状态码
  2. 网络畅通,服务器没有接受到请求,返回404状态码
    检查请求地址是否错误
  3. 网络畅通服务器端能接受到请求,服务器端返回500状态码
    服务端错误,后端的锅
//当网络中断时会触发onerror事件
xhr.onerror = function(){
    alert('网络中断')
}
  • 低版本ie的缓存问题

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

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

    xhr.open('get'.'localhost:8000/index?t=' + Date.now())
    

Ajax异步编程

  • 概述

    同步:

    • 上一行代码执行完之后,才能执行下一行代码,代码逐行执行

      console.log('before');
      console.log('after');
      

    异步:

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

      console.log('before')
      setTimeout(()=>{
          consol.log('last')
      })
      console.log('after')
      

ajax封装

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

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

  function ajax({type, url, data, header, success, error}){
    const xhr = new XMLHttpRequest();
    //定义一个拼接参数的变量
    let params = '';
    for(let item in data) {
      //将参数转换成字符串格式
      params += item  + '=' + data[item] + '&'
    }
    //去掉参数最后面的&符
    params = params.substr(0, params.length - 1)
    //如果请求方式为get,将请求参数拼接在url后面
    if(type == 'get') {
      url = url + '?' + params;
    }
    //配置ajax对象
    xhr.open(type, url);
    //如果请求参数为post,设置请求参数格式类型,并在发送请求时将参数传递进去
    if(type == 'post') {
      //用户希望向服务器端传递请求参数的类型
      const contentType = header['Content-Type'];
      xhr.setRequestHeader('Content-Type', header['Content-Type']);
      //判断用户设置的请求参数类型
      if(contentType == 'application/json') {
        //如果是json格式,直接传递参数json字符串
        xhr.send(JSON.stringify(data))
      } else {
        //发送请求
        xhr.send(params);
      }
    } else {
      //发送请求
      xhr.send();
    }
    //监听xhr对象下的onload事件,当xhr对象接收完响应数据后触发
    xhr.onload = function(){
      //获取响应头中的数据
      const resContentType = xhr.getResponseHeader('Content-Type') //参数为要获取的类型
      //服务端返回的数据
      let response = xhr.responseText
      //判断返回的数据是否是JSON字符串
      if(resContentType.includes('application/json')) {
        response = JSON.parse(response)
      }
      //判断http状态码,等于200时调用处理成功情况的函数
      if(xhr.status == 200) {
        success(response, xhr);
      } else {
        //否则调用请求失败的函数
        error(response, xhr)
      }
    }
  }

模板引擎

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

    <script src="./js/template-web.js"></script>
    
  2. 准备art-template模板

    <script id="tpl" type="text/html">
        <div class="box"></div>
    </script>
    
  3. 告诉模板引擎将哪一个模板和哪个数据进行拼接

    let html = template('tpl',{username: 'zhangsan', age: 20})
    
  4. 将拼接好的html字符串添加到页面中

    document.getElementById('container').innerHTML = html
    

FormData

  • 作用

    1. 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数格式
    2. 异步上传二进制文件
  • 使用

    <!-- html中 -->
    
    
    
    <form action="" id="form">
      <input type="text" name="username">
      <input type="password" name="password" id="">
      <input type="button" id="btn" value="提交">
    </form>
    <script>
      //获取按钮
      const btn = document.querySelector('#btn')
      //获取表单
      const form = document.querySelector('#form')
      btn.addEventListener('click',function(){
        //将普通的html表单转换成表单对象
        const formData = new FormData(form)
        //创建ajax对象
        const xhr = new XMLHttpRequest()
        //配置ajax
        xhr.open('post', 'http://localhost:8000/form')
        //发送ajax请求
        xhr.send(formData)
        //监听xhr的onload事件
        xhr.onload = function(){
          //判断状态码
          if(xhr.status == 200) {
            console.log(xhr.responseText)
          }
        }
      })
    </script>
    
    //app.js
    
    //引入formidable
    const formidable = require('formidable')
    
    
    app.post('/form', (req, res)=> {
      //创建表单解析对象
      const form = new formidable.IncomingForm()
      form.parse(req,(err, fields , files)=> {
        res.send(fields)
      })
    })
    

formData对象的实例方法

  1. 获取表单对象中的属性值

    formData.get('key') //key是表单name属性的值
    
  2. 设置表单对象中属性的值

    如果设置的表单属性存在,将替换原有的值
    如果不存在,将会创建这个表单属性

    formData.set('key', 'value') //key是表单name属性的值,value为将要设置的值
    
  3. 删除表单对象中的属性

    formData.delete('key')
    
  4. 向表单对象中追加属性值

    formData.append('key', 'value')
    

**注意:**set方法与append方法的区别是,在 属性名已存在的情况下,set会覆盖已有键名的值,append会保留两个值

formData二进制文件上传

<input type="file" id="file"/>
const file = document.querySelector('#file')
//监听表单change事件(用户选择文件的时候)
file.onchange = function(){
    //创建空表单对象
    let formData = new FormData()
    //将用户选择的文件追加到formData对象中
    formData.append('attrName', this.files[[0]])
    //创建ajax对象
    let xhr = new XMLHttpRequest()
    //对ajax对象进行配置
    xhr.open('post', 'http://localhost/8000/upload')
    //发送ajax请求
    xhr.send(formData)
    //监听服务端响应给客户端的数据
    xhr.onload = function(){
        //判断状态码
        if(xhr.status == 200) {
            
        }
    }
}
  • 文件上传进度展示

    监听xhr对象下的upload下的progress事件,事件对象e中的loaded代表已上传的文件大小,total代表文件总大小

    <style>
        #out {
          width: 800px;
          height:20px;
          background-color: rgba(245,245,245,.9);
          text-align: center;
          color: red;
        }
        #insert {
          width: 0%;
          height:100%;
          background: aquamarine;
        }
      </style>
    
    
    <form action="">
      <input type="file" name="attrName" id="file">
    </form>
    <div id="out">
      <div id="insert">0%</div>
    </div>
    
    const file = document.querySelector('#file')
      const insert = document.querySelector('#insert')
      //监听表单change事件(用户选择文件的时候)
      file.onchange = function(){
        //创建空表单对象
        let formData = new FormData()
        //将用户选择的文件追加到formData对象中
        formData.append('attrName', this.files[[0]])
        //创建ajax对象
        let xhr = new XMLHttpRequest()
        //对ajax对象进行配置
        xhr.open('post', 'http://localhost:8000/upload')
        //监听上传进度信息
        xhr.upload.onprogress = function(e){
          let res = parseInt((e.loaded / e.total) * 100) + '%'
          console.log(res);
          insert.style.width = res
          insert.innerHTML = res
        }
        //发送ajax请求
        xhr.send(formData)
        //监听服务端响应给客户端的数据
        xhr.onload = function(){
          //判断状态码
          if(xhr.status == 200) {}
        }
      }
    

Ajax请求限制

  1. cookie无法读取
  2. DOM无法获取
  3. Ajax请求可以发送,但响应的数据被拦截
  • 同源策略
    如果连个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源

JSONP解决跨域

JSONP(json with padding),是一个非官方的跨域解决方案,只支持get请求,利用的是标签请求资源不受同源策略限制的特点

步骤:

<button id="btn">点击发送请求获取数据</button>
<script>
//获取需要监听事件的元素
  let btn = document.querySelector('#btn')
//监听事件
  btn.addEventListener('click', function(){
    // 1.动态创建script标签
    let scriptNode = decument.createElement('script')
    //2.定义一个处理数据的函数(必须是全局作用域下的函数)
    window.getData = function(data){
      console.log(data)
    }
    //3.给动态创建的script标签加上请求地址(callback是要使用数据的函数的名)
    scriptNode.src = 'http://localhost:8000/getData?callback=getData'
    //4.将标签放入页面,请求数据
    document.body.appendChild(scriptNode)
  })
</script>

关于jsonp解决跨域的说明:

  • 原理:利用了标签发送GET请求“天然跨域”(不受同源策略的限制)
  • 套路:
    • 创建script节点,指定src
    • 定义好一个数据处理函数
    • 把数据处理函数的名称传递给后端
    • 后端返回符合js函数调用语法的字符串
  • 局限性:
    • 只能解决GET请求跨域问题
    • 必须需要后端配合(配合返回调用函数的字符串)

cors解决跨域(服务端允许跨域请求)

app.use((req, res, next)=> {
    //第二个参数为  *  则允许所有网站跨域请求
    res.header('Access-Control-Allow-origin', 'http://localhost:3000')
    //第二个参数为允许的跨域请求方式
	res.header('Access-Control-Allow-Methods', 'get, post')
    next()
}) 

withCredentials属性

在使用ajax技术发送跨域请求时,默认情况不会在请求中携带cookie信息(出于对安全性的考虑)

**xhr下的属性:**withCredentials:指定在涉及到跨域请求时们是否携带cookie信息,默认值为false

xhr.withCredentials = true

**服务端响应头:**Access-Countrol-Allow-Credentials:true允许允许发送请求时客户端携带cookie

res.header('Access-Control-Allow-Credentials', true)

JQuery中的$.ajax()

$.ajax({
    type: 'get',
    //如果协议、域名、端口,都相同的情况下,不必写完整的请求地址
    url: 'http://localhost:3000',
    //也可以传递字符串参数值,'name=zhangsan&age=18'
    data: {name: 'zhangsan', age: '20'},
    //如果需要传递JSON格式,将下面设置为'application/json'
    contentType: 'application/x-www-form-urlencoded',
    //请求发送之前需要做的事,例如表单格式验证,如果不符将return false,阻止发送请求
    beforeSend: function(){return false},
    //请求成功被调用
    //返回的数据:会根据服务端返回的数据类型,自动转换为相应的类型
    success: function(response){},
    //请求失败被调用
    //接收ajax对象做参数,处理错误信息
    error: function(xhr){}
})

serialize()方法

将表单内容拼接成字符串类型的参数

$('#form').serialize()
//username=zhangsan&age=20

封装将表单内容转换成对象的方法

function(obj){
let result = {};
//将表单数据转换成数组,例:[{name: 'username', value: 'zhangsan'}, {name: age, value: '20'}]
let params = obj.serializeArray();
//循环这个数组将数组转换成对象
$.each(params, function(index, value){
    result[value.name] = value.value;
})
    return result
}

发送jsonp请求

$.ajax({
    url: 'http://localhost:3000',
    //代表发送的是jsonp请求
    dataType: 'jsonp',
    //可选参数:修改向服务器传递函数名的参数的名称(默认是callback)
    jsonp: 'cb',
    //可选参数:指定函数名称(一般用不上)
    jsonCallback: 'fnName',
    success: function(response){}
})

$.get()$.post()

//第二个向服务器传递参数的参数为可选参数,可以是字符串或对象
$.get('http://localhost:3000', {name: 'zhangsan'}, function(response){})

$.post('http://localhost:3000', {name: 'zhangsan'}, function(response){})

ajax全局事件

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

//当请求开始发送时触发
$(document).on('ajaxStart',function(){})()
//当请求完成时触发
$(document).on('ajaxComplete',function(){})()

NProgress进度条插件

官宣:纳米级进度条,使用逼真的涓流动画来告诉用户正在发生的事

<!--引入相应的js与css文件-->
<link rel="stylesheet" href="nprogress.css">
<script src="nprogress.js"></script>
Nprogress.start()//进度条开始运动
NProgress.done()//进度条结束运动
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值