【JavaScript】15. 浅析Ajax

270 阅读7分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

关于Ajax

01. Ajax

(1)是什么

  • Ajax 是一种用于创建快速动态网页的技术
  • Ajax用来与后台交互
  • Ajax可以实现页面无刷新更新数据
  • Ajax的API中核心提供的是一个XMLHttpRequest类型,所有的AJAX操作都需要使用这个类型

(2)怎么做?

创建一个新的HTTP请求
  • 指定请求的methods、URL,以及是否异步处理请求:xhr.open(methods, url, true)

    • methods:请求方法
    • url:请求地址
    • boolean:用于设置此次请求是否采用异步方式执行
      • true——异步;false——同步

    同步执行,代码会卡在xhr.send()这一步,等到所有的数据都传输完成,才会往下执行

    且,全部的数据传输完成后,readystate将不再变化

    因此,onreadystatechange事件不会被触发

    • onreadystatechange事件,只有在readystate变化的时候才会被触发
    • 可以在send()方法之前注册事件
请求参数
  • GET请求:拼接在URL后面

    xhr.open("get","请求地址?" + params)
    
    
  • POST请求:请求体中

    // application/x-www-form-urlencoded
    var params = "name=" + name + "&age=" + age 
    
    // application/json
    var params = {
        name: 'name',
        age: 'age'
    }
    xhr.send(params)
    
    
服务器响应
  • onload

    xhr.onload = () => {
        let responseText = JSON.parse(xhr.responseText)
        console.log(responseText)
    }
    
  • onReadyState

    • 一般都是在readyState == 4时,执行响应的后续逻辑
    // 当ajax状态码发生变化的时候触发
    xhr.onreadystatechange = () => {
        console.log(xhr.readyState)
    }
    
    

(3)封装

  • 基本步骤
// 1.创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest()

// 2.创建一个新的HTTP请求,并指定请求的methods、URL,以及是否异步处理请求
xhr.open('GET', url, true)

// 3.设置响应头请求内容编码类型
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")

// 4.发送HTTP请求
xhr.send(null)

// 5.获取服务器响应数据
xhr.onreadystatechange = function () {
    if(this.readyState === 2){       
        // 接收到响应头       
        console.log("HEADERS_RECEIVED", xhr.readyState)
    }else if(this.readyState === 3){        
        // 响应体加载中       
        console.log("LOADING", xhr.readyState)
    }else if(this.readyState === 4){        
        // 加载完成        
        console.log("DONE", xhr.readyState)
    }
}


  • 方法
function ajax(options){
    // 设置默认值
    var defaults = {
        type: "get",
        url: "",
        data: {},
        header: {
            "content-Type": "application/x-www-form-urlencoded"
        },
        success: function(){},
        error: function(){}
    };
    // 浅拷贝,如果设置了对应属性的值,则使用该值,没有设置属性值,则使用默认值
    Object.assign(defaults, options)

    // 创建ajax对象
    var xhr = new XMLHttpRequest()
    // 拼接请求参数的对象
    var params = "";
    for(var attr in defaults.data){
        // 将参数转换为字符串格式
        params += attr + "=" + defaults.data[attr] + "&"
    }
    // 截取字符串,取消最后一个 & 符号
    params = params.substr(0, params.length-1)
    // 判断请求方式
    if(defaults.type == "get" ){
        // 如果是GET请求,就将参数拼接在请求地址中
        defaults.url = defaults.url + "?" + params
    }
    // 配置ajax对象
    xhr.open(defaults.type, defaults.url)
    // 发送请求
    if(defaults.type == "post"){
        // POST请求---
        // 用户希望向服务器端传递的请求参数的类型
        var contentType = defaults.header["Content-Type"]
        // post请求必须设置请求报文
        xhr.setRequestHeader("Content-Type",contentType)
        // post请求要send参数
        // 判断类型
        if(contentType == "application/json"){
            // 如果是JSON格式,需要将传进来的JSON对象,转换为JSON字符串
            xhr.send(JSON.stringify(defaults.data))
        }else{
            // 传递拼接的字符串参数
            xhr.send(params)
        }
    }else{
        // GET请求---
        xhr.send()
    }
    // 监听onload事件
    xhr.onload = function(){
        // xhr.getResponseHeader(),获取响应头中的数据
        var contentType = xhr.getResponseHeader("Content-Type")
        
        // 获取服务器端返回的数据
        var responseText = xhr.responseText
        
        // 如果响应类型中包含application/json,即为JSON数据格式
        if(contentType.include("application/json")){
            // 将从服务器端响应得到的数据,从JSON字符串格式转换为JSON对象格式
            responseText = JSON.parse(responseText)
        }
        
        // 判断请求是否成功
        if( xhr.status == 200){
            defaults.success(responseText, xhr)
        }else{
            defaults.error(responseText, xhr)
        }
    }
}

ajax({
    // 请求方式
    type: "get",
    // 请求地址
    url: "URL",
    // 请求参数
    data: {
        name: "Ruovan", 
        age: 24
    },
    // 请求报文类型
    header: {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    // 请求成功
    success: function(data) {
        console.log(data)
    },
    // 请求失败
    error: function(data, xhr){
        console.log(data)
        console.log(xhr)
    }
})


02. 关于JSON

  • 字符串转对象:JSON.parse()
  • 对象转字符串:JSON.stringify()

03. 关于请求方式

  • GET

    • 一般用于信息获取,使用URL传递参数
    • GET请求参数在URL中
    • GET速度比POST块
    • 对所发送信息的数量也有限制,一般在2000个字符
  • POST

    • 一般用于修改服务器上的资源
    • 需要设置请求头,规定请求数据的类型
    • POST请求参数才请求体中,POST比GET更安全
    • 对所发送的信息没有限制

  • GET请求需要使用Request.QueryString来取得变量的值
  • POST请求方式通过Request.Form来获取变量的值
    • 也就是说Get是通过地址栏来传值,而Post是通过提交表单来传值

  • 然而,在以下情况中,请使用 POST 请求:

    • 无法使用缓存文件(更新服务器上的文件或数据库)
    • 向服务器发送大量数据(POST 没有数据量限制)
    • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

04. 关于跨域

(1)产生原因?

  • 浏览器的同源政策导致的,无法向非同源地址发送Ajax请求,如果请求,浏览器就会报错
    • 协议、域名、端口、IP的不同,都会导致跨域

(2)如何解决?

jsonp:只能解决GET跨域
  • 动态创建一个<script>标签,利用<script>标签的src属性不受同源策略限制的特性

    所有的srchref属性都不受同源政策的限制,可以请求第三方服务器数据内容

    比如:外部的图片资源,外部的JS文件等等

  • 服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数

// 1. 动态创建一个script标签
let script = document.createElement("script")

// 2. 设置script标签的 src 属性为 接口地址/服务器地址 并带一个 callback 回调函数名称
script.src = "http://127.0.0.1:8888/index.php?callback=jsonpCallback"

// 3. 将script标签 插入到页面
document.head.appendChild(script)

// 4. 通过回调函数 去接收后台返回数据
function jsonpCallback(data){
    // jsonp返回的数据是json对象,可以直接使用
    // 但如果是 ajax 取得数据是json字符串,需要转换成json对象才可以使用
}


CORS跨域资源共享
  • 原理:服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求

  • 限制:浏览器需要支持HTML5,可以支持POST,PUT等方法,兼容ie9以上

// 在服务器设置
// 允许所有域名访问
Access-Control-Allow-Origin: "*"
    
// 只允许指定域名访问
Access-Control-Allow-Origin: "http://a.com"

document.domain
  • 原理:相同主域名不同子域名下的页面,可以设置document.domain让它们同域
  • 限制:同域document提供的是页面间的互操作,需要载入iframe页面
// URL http://a.com/foo
// 1. 创建 iframe 标签
var ifr = document.createElement('iframe')

// 2. 设置src属性为服务器地址
ifr.src = 'http://b.a.com/bar'

ifr.onload = function(){
    var ifrdoc = ifr.contentDocument || ifr.contentWindow.document
    ifrdoc.getElementsById("foo").innerHTML)
};
// 4. 隐藏 iframe
ifr.style.display = 'none'

// 5. 插入页面
document.body.appendChild(ifr)

Apache
  • 做转发(逆向代理),让跨域变成同域
服务器解决
  • 同源政策是浏览器给予Ajax技术的限制**,服务器端不存在同源政策限制**
  • 首先让1号客户端1号服务器端发送请求
  • 然后由1号服务器端访问2号服务器端的数据
  • 最后让1号服务器端获取的数据响应给1号客户端

05. 关于状态码

  • 2开头状态码

    • 表示成功处理了请求的状态代码
  • 3开头的状态码

    • (重定向) 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。
  • 4开头的状态码

    • 表示请求可能出错,妨碍了服务器的处理
    message = '400:请求错误'
    
    message = '401:未授权,请重新登录'
    
    message = '403:拒绝访问'
    
    message = '404:请求资源不存在'
    
    message = '405:请求方法不允许'
    
    message = '408:请求超时'
    
    
  • 5开头的状态码

    • 码表示服务器在尝试处理请求时发生内部错误。
      • 这些错误可能是服务器本身的错误,而不是请求出错
      • 也可能是请求参数错误,服务器无法处理这个参数
    message = '500:内部服务器错误'
    
    message = '501:服务未实现'
    
    message = '502:网络错误'
    
    message = '503:服务不可用'
    
    message = '504:网络超时'
    
    message = '505:HTTP版本不受支持'
    
    

    本人前端小菜鸡,如有不对请谅解