原生ajax、jq的ajax、axios、promise、jsonp

补充:XMLHttpRequest对象

XMLHttpRequest是一种支持异步请求的技术,是ajax的核心。

作用

  1. 可向服务器提出请求并响应,而不阻塞用户
  2. 可在页面加载后进行页面的局部更新

ajax初步认识

ajax 即 Asynchronous Javascript And XML.就是异步的javascript和XML (数据的传输格式,但现在一本用json),是一种创建交互式网页应用的网页开发技术。可在不刷新页面的前提下向后端发送/请求数据。通常用在前后端数据传送的过程中。

但传数据也可以用form表单,但传送时不可避免会刷新页面。

一、原生ajax

/**
 * 要完整实现一个ajax异步调用和局部刷新的步骤:
 * 1.创建XMLHttpRequest对象,即一个异步调用对象
 * 2.创建一个新的HTTP请求,并指定该请求的方法和url,用open方法(method,url,async)
 * method:请求类型,GET或POST;url:文件在服务器上的位置,必须指定;async: true(异步)false(同步)。open方法不会向服务器发送真正的请求,他相当于初始化请求并准备发送只能向同一个域中使用相同协议和端口的url发送请求,否则会因为安全原因报错
 * 3.设置响应HTTP请求状态变化的函数
/**
* 必须使用POST请求的情况:
 * 1.无法使用缓存文件(即更新服务器上的文件或数据库)
 * 2.向服务器发送大量数据(POST无数据量限制)
 * 3.发送包含未知字符的用户输入时,POST比GET更稳定可靠
 * 同步:提交请求-》等待服务器处理-》处理完毕返回,期间客户端浏览器不能干任何事
 * 异步:请求通过事件触发-》服务器处理(浏览 器仍然可以做其他事)-》处理完毕
 *
*/
 // 封装通用的创建xhr对象函数,兼容各个版本
        function createXHR(){
            // 判断浏览器是否将XMLHttpRequest作为本地对象实现,针对IE7、firefox、opera等
            if(typeof XMLHttpRequest != 'undefined' ){
                return new XMLHttpRequest()
            } else if(typeof ActiveXObject != 'undefined'){     // 低版本ie是ActiveXObject
                // 将所有可能出现的ActiveXObject版本放在一个数组中
                var xhrArr = ['Microsoft.XMLHTTP','MSXML2.XMLHTTP.6.0','MSXML2.XMLHTTP.5.0','MSXML2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP.2.0']
                // 遍历创建XMLHttpRequest对象
                var len = xhrArr.length,xhr
                for(var i=0;i<len;i++){
                    // 只需去判断哪个版本可运行即可,不是对其真假的判断,所以用trycatch
                    try{
                        // 创建ActiveXObject对象,创建实例时,括号中写版本号
                        xhr = new ActiveXObject(xhrArr[i])
                        break       // 找到可运行的版本后,可直接退出循环
                    }
                    catch(ex){          // 捕获错误

                    }
                }
                return xhr
            } else {                    // 数组中的版本都不支持xhr对象
                throw new Error('No XHR object available!')
            }
        }
        创建实例时,直接 var xhr = createXHR()即可
// ajax需要在服务器环境运行,所以在执行时,vscode要安装live server插件,选择 open with live server
        // 1. 创建实例
        let xhr = new XMLHttpRequest()

        // 创建实例后,用readystate请求当前状态来监听事件进度
        xhr.onreadystatechange = function () {
            // console.log(this.readyState);
            // readyState有四种状态:
            // 数字1 : 请求建立(open执行后)
            // 数字2 : 请求发送(send执行后)
            // 数字3 : 请求已发送,正在等待处理
            // 数字4 : 请求完成,请求成功或失败,都是显示4.且一般都是在4的情况下,执行后续代码
            if (this.readyState === 4) {
                console.log(this.responseText);  // 拿到后端返回的数据,其为字符串格式
                // 返回的是个图片时,得到的时图片的地址
                // 返回的数据使用
                 let oImg = new Image()
                 oImg.src = this.responseText
                 document.body.appendChild(oImg)
				   /**
                     * 在收到响应后相应数据会填充到XHR对象的属性,有四个属性会被填充:
                     * 1、responseText---从服务器进程返回数据的字符串形式
                     * 2、responseXML---从服务器进程返回的DOM兼容的文档数据对象
                     * 3、status---从服务器返回的数字代码,如404(未找到)和200(已就绪)
                     * 4、status Text---伴随状态码的字符串信息
                    */
            }
        }

        // 2. 创建请求,xhr.open(method,接口地址,是否异步)
        // 参数一: 请求方式,分为GET/POST,实际开发中,后端提供
        // 参数二: 接口地址(http:xx.xx.com/img)。上线时,若前后端在同一服务器环境下,可直接用服务器路径。如:/img.  后端提供
        // 参数三: 是否异步,true为异步,false为不异步。固定true
        xhr.open("GET","/lujing",true)

        // 3.发送请求
        xhr.send()


// GET 传参时,在xhr.open()的接口路径后加?内容即可,如?name=csdn&age=18
// post传参时,要设置一下请求头数据格式
		 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		xhr.send("name=csdn&age=18")

获取的数据为字符串类型,但是看起来很像数组,要将其处理成对象。方法:通过Json对象来处理。但前提是后端返回的数据是json格式的数据。(jq的ajax、axios都会自动处理)

        // json对象处理数据,再执行后续的前端逻辑代码
        let data = JSON.parse(xhr.responseText)
        /**
        * json对象的stringify()和parse()分别用于把javascript对象序列化为json字符串和把json字符串解析为原生javascript值
       * javascript的evel()类似于json.parse()方法,可将json字符串转换为json对象,但是evel()可以执行不符合json格式的代码,会包含恶意代码,少用。json大写
        */ 
        // json格式示例:
        let a = {
            "name" : "csdn" ,
            "age" : 18 ,
            "type" : "website" ,
            "friend" : {
                "name" : "everybody",
                "isfriend" : true
            }
        }

二、jquery的ajax

    <!-- 1. 引入jquery文件,此处引入的是网上发布的一个,例: -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js">
    </script>

		// 数据
		let x = {
            name : "csdn",
            age : 18
        }

		$.ajsx ({
            type : "GET",  // 请求方式,默认GET,POST相同
            url : "url地址",  // 地址,地址中需要汉字时,要用encodeURI()处理,转成URI编码,decodeURI()可将URI编码转化成汉字
            data : x , // 所传数据,也可直接{name:"csdn",age:18}
            dataType : 'json' , // 按json格式预处理后端传来的数据
            success : function (datameg){       // 同 success(msg){}
                console.log(datameg);    // msg为处理过后的后端数据
            },  // 成功之后的回调函数
            error : function (){            // 同 error (){}
            }   // 错误时的回调函数
        })

推荐一个测试接口网站:接口测试


        // 1、get请求
        $.ajax({
            type: 'GET',
            url : 'http://jsonplaceholder.typicode.com/users',    // 测试地址
            data : '',
            dataType : '',
            success : function (data){
                console.log(data);
            },
            error : function (){}
        })
        // 2、post请求
        $.ajax({
            type : 'POST',
            url : 'http://jsonplaceholder.typicode.com/users', 
            data : {name:'gll', age: 13, email: '3151805608@qq.com'},
            dataType : 'json',
            success : function (data){
                console.log(data);
            },
            error : function(){}
        })

三、Promise

Promise是ES6新增的一个API,可解决异步代码回调函数层级深的问题。回调函数就是在某件事完成后必须要执行的函数,如上面的success和error。但是函数A嵌套函数B,B嵌套C…,会造成回调地狱的情况,在代码本身来看就是代码横向发展,导致左侧空白,一直向右边延伸。Promise只解决代码感官上的问题,不解决实际问题。

注意: 实际操作中,忌讳将两种类型代码糅合在一起,比如将DOM结构写入ajax代码,降低可读性。

为了避免这种忌讳,就会出现只在ajax代码中执行DOM中的函数,例如函数名为fly(),在读代码时,还要去找函数fly()中的内容,代码多时,很难。

	// 回调地狱

		setTimeout (() => {
            console.log("定时器1");
            setTimeout(() => {
                console.log("定时器2");
                setTimeout(() => {
                    console.log("定时器3");
                    //.....
                }, 2000);
            }, 2000);
        },2000)
        // 解决方法一:各函数间的关联视觉上小
        function s1 (){
            setTimeout(() => {
                console.log("定时器1");
                s2()
            }, 2000);
        }
        function s2 (){
            setTimeout(() => {
                console.log("定时器2");
                s3()
            }, 2000);
        }
        function s3 (){
            setTimeout(() => {
                console.log("定时器3");
               // s4()
            }, 2000);
        }
        // ....
        // 解决方法二
        new Promise((res,rej) => {          // reslove 成功; reject  失败
            setTimeout(() => {
                console.log("定时器1");
                res();      // 执行的是res(),就执行then()
            }, 2000);
        }).then ((res, rej) => {            // 不需执行res或rej时,参数可不写         
            setTimeout(() => {
                console.log("定时器2");
                //rej()      // 执行的是rej(),就执行catch()
                res()
                return new Promise()        // 只有Promise才有then,所以要返回Promise
            }, 2000);
        }).then (() => {
            setTimeout(() => {
                console.log("定时器3");
            }, 2000);
        }).catch (() => {
            console.log("捕获失败");
        })
        // 一般API会自动将函数封装,然后确保每一步返回的都是Promise
        function retPro(str) {
            return new Promise ((res, rej)=>{
                setTimeout(() => {
                    console.log(str);
                    res()
                }, 2000);
            })
        }
        // 调用
        retPro("定时器1").then(()=>{
            return retPro("定时器2")
        }).then(()=>{
            return retPro("定时器3")
        })
        let promise1 = new Promise((resolve,reject) => {
            // resolve()        // 走.then
            reject()            // 走.catch
        })
        console.log(promise1);
        promise1
            .then( () => console.log("成功,无任何问题"))
            .then( () => console.log("成功,可无限调用then方法"))
            .catch( () => console.log("出问题了"))
		// promise中有promise
        let p1 = new Promise((resolve, reject) => {
            reject()
        })
        p1
            .then( () => console.log("I am p1"))
            .catch( () => console.log("error 1"))

        let p2 = new Promise( (resolve, reject) => {
            resolve (p1)    //执行p2时,要观察返回的p1是什么状态,此处p1返回的是失败的状态,纵使P2执行成功函数,也是走失败的函数
        })
        p2
            .then( () => console.log("I am p2"))
            .catch( () => console.log("error 2"))
        let p1 = new Promise((resolve, reject) => {
            resolve(p2) // 此处会去执行p2,但p2的代码在下面,表明没有,要去找,执行完p2后;执行p1时,会走失败的代码。调换一下代码顺序,就会都执行成功的代码。
        })
        p1
            .then( () => console.log("I am p1"))
            .catch( () => console.log("error 1"))

        let p2 = new Promise( (resolve, reject) => {
            resolve ()
        })
        p2
            .then( () => console.log("I am p2"))
            .catch( () => console.log("error 2"))

四、fetch

        // POST有个parmas属性用来传数据
        fetch("/url", {method: "GET"})     // 固定两个then
            .then((res)=>{            // 第一个then,格式化
                return res.json()  // 格式化返回的数据
            })
            .then((data) =>{           // 第二个then,传数据
                console.log(data);      // 所得数据
        })
        // 基于标准的promise对象进行封装的
        let url = 'http://jsonplaceholder.typicode.com/users'
        // console.log(fetch(url));
        // fetch(url).then(data => console.log(data))  // 此处返回的是response,即响应的信息,要提取出数据,用下面
        fetch(url)
            .then(res => res.json())    // 将response转换成json格式
            .then(data => console.log(data))    // 从json格式中提取出数据

五、axios(用的比较多)

本质就是一个用来发送ajax请求的包。

    <!-- 静态页面引入axios包 -->
    <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>

		// get
       axios.get("url",{params:数据})     // 这里返回的是一个Promise,才有then
       .then(res =>{
           console.log(res);        // res里是axios封装好的一个对象,包括data、status(http状态码)、statusText(状态码文本格式)、请求头、配置、请求信息
           console.log(res.data);      // res.data才是后端返回的数据
       })

       // 上面也可解构赋值写成
       .then({data} => {
           console.log(data);
       })

		// post
       axios.post("requrl",shuju)
       .then(res =>{
           console.log(res.data);
       })

       // axios可设置默认url,操作就都基于这个url,例:
       axios.defaults.baseURL = "http:// 123.345.456.567/"

六、跨域

在当前服务域环境下向另一个服务域请求访问。(能在浏览器地址栏输入得到的都是get请求)

两个服务域是否为同一个,判断

  • 协议 http、https
  • ip 公网ip,本地ip / ip4、ip6
  • 端口 自己使用时,端口最好在2000以上,1-2000可能已被windows本身的服务占用

三者都相同,为同域,有一个不同,为跨域。

解决跨域问题的四种方法:
1、跨域资源共享(CORS)
2、使用jsonp(常用)
3、修改document.domain
4、使用window.name

七、jsonp

jsonp只是一种解决跨域问题的方法,由回调函数和数据组成。回调函数就是响应到来时页面要调用的函数;数据就是传入回调函数的json格式数据

jsonp的原理:直接用XMLHttpRequest请求不同域上的数据是不可以的,但是在页面上引入不同域上的js脚本文件却是可以的
即:通过script标签引入js文件 —》js文件载入成功后 —》基于接口执行我们在url参数中指定的函数

后端返回数据的格式若为jsonp格式,可使用jsonp来跨域获取数据。

        // jsonp数据格式示例
        jsonp([{
            "name" : "csdn",
            "age" : 18,
            "type" : "website"
        }])
// 这个就是在网站搜索mm后,浏览器臆想出来的你可能要搜索的问题(联想关键字),在network->xhr可以找到。其就是jsonp格式。且其连接也不同,会在后边加上数字,目的是访问时间不同,数据也不同,防止低版本ie浏览器缓存。链接中的cb = ...就是函数名字jQuery11020574872738612938_1609319085128,cb即(callback);wd=...即输入的词。

jQuery11020574872738612938_1609319085128({
    	"q":"mm",
    	"p":false,
       	"g":[{"type":"sug","sa":"s_1","q":"mm是什么单位的"},			  
             {"type":"sug","sa":"s_2","q":"mm是厘米还是毫米"},
             {"type":"sug","sa":"s_3","q":"mm131. net"},
             {"type":"sug","sa":"s_4","q":"mmo游戏是什么意思"},
             {"type":"sug","sa":"s_5","q":"mms是什么短信"},
             {"type":"sug","sa":"s_6","q":"mm是什么意思"},
             {"type":"sug","sa":"s_7","q":"mma格斗"},
             {"type":"sug","sa":"s_8","q":"mmc战神录"},
             {"type":"sug","sa":"s_9","q":"mm豆"},
             {"type":"sug","sa":"s_10","q":"mmol/l是什么单           		
              位"}],"slid":"4306614745162910723","queryid":"0xcc2b4b15e50c03"})
// 要想使用这些数据,直接引入url地址即可(地址可精简成 https://www.baidu.com/sugrec?prod=pc&wd=mm&cb=jQuery1102027511459668537475_1609320904781 ),因为他返回的就是一个js文件,可执行,但要确保使用时,有函数名对应的函数,且有形参接收传送的数据
              
        function jQuery1102027511459668537475_1609320904781(data) {
            console.log(data);
        }
       <script src="https://www.baidu.com/sugrec?prod=pc&wd=mm&cb=jQuery1102027511459668537475_1609320904781"></script>

在这里插入图片描述

封装jsonp函数解决跨域问题
        // 封装jsonp
        function getJSONP(url,callback){
            if(!url){
                return;
            }
            // http://www.baidu.com,由于jsonp数据的格式,要对url做处理,添加参数。如:http://www.baidu.com?id=aaa,返回的就是aaa中的json格式数据;还有一种情况就是后面已有参数,就不能用?了,要用&。
            // 声明数组用来随机生成函数名
            var a =['a','b','c','d','e','f','g','h','i','j'],
                r1 = Math.floor(Math.random()*a.length),     // a.length=10,生成0-9之间的随机数
                r2 = Math.floor(Math.random()*a.length),
                r3 = Math.floor(Math.random()*a.length),
                name = 'gll' + a[r1] + a[r2] + a[r3],     // 前缀是gll
                cbname = 'getJSONP.' + name        // 要使它成为getJSONP的一个属性或者方法,一定要是getJSONP.
                // console.log(name);      // 生成例如:gllcch的name,要将他设为getJSONP的一个属性
                // console.log(cbname);    // 生成例如:getJSONP.gllife,服务器返回的就是getJSONP.gllife()中的json数据。我们将cbname传给服务器,必须要加getJSONP.的前缀。
                // 判断url中是否含有?号
                if(url.indexOf('?') === -1){        // url中无?号
                    url += '?jsonp=' + cbname       // http://www.baidu.com?jsonp=getJSONP.gllggf,可见我们传url给服务器,解析jsonp,发现jsonp等于getJSONP.gllggf,而getJSONP.gllggf是我们自己设的,他返回的就是getJSONP.gllggf()中的json数据,所以要对gllggf操作。
                } else {
                    url += '&jsonp=' + cbname
                }
                // console.log(url);

                // 动态创建script标签
                var script = document.createElement('script')
                //定义被脚本执行的回调函数
                getJSONP[name] = function(data){    // 此处用.会导致name成为一个固定的字符串,数据被data接收;我们在浏览器要求显示json数据时,首先得到的是abc([]),js理解为调用abc函数,里面的json数据被data接收到,所以定义这个函数;jsonp接口形式: 字符串([])
                    try{
                        callback && callback(data)     // 即传入的回调函数,回调函数存在就执行
                    } catch(e){             // 错误信息被e接收

                    }finally{               // 不论成功还是捕获到错误,都执行finally函数
                        // 多次后会生成多个函数及script标签,最后删除该函数即script标签
                        delete getJSONP[name]
                        script.parentNode.removeChild(script)
                    }
                }

                // 定义script的src
                script.src = url
                document.getElementsByTagName('head')[0].appendChild(script)
        }

        // 调用时,传入url地址,函数接收,执行
        getJSONP("http://class.imooc.com/api/jsonp",function(data){
            console.log(data);
        })  
        

效果图:
在这里插入图片描述
直接输入连接:(可见名字是随机的)
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值