第9章 JS-Web-API-Ajax【每个工程师必须熟练掌握的技能】

返回章节目录

目录

1.XMLHttpRequest

get请求

流程和api说明

post请求

2.同源策略和跨域

同源策略

可以不遵守同源策略的情况

跨域

3.jsonp和cors

引出问题

jsonp的基本实现原理

你真的了解jsonp的请求过程吗(例子均为gif演示)

jQuery演示jsonp

vue演示jsonp

cors

4.练习题

1.手写一个简易的ajax


1.XMLHttpRequest

get请求

ajax.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>ajax 演示</title>
    </head>
    <body>
        <p>一段文字 1</p>

        <script src="./ajax.js"></script>
    </body>
</html>

ajax.js

const xhr = new XMLHttpRequest()
xhr.open('GET', '/data/test.json', true) // true为异步,false为同步
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            console.log(JSON.parse(xhr.responseText))
            alert(xhr.responseText)
        } else {
            console.log('其他情况')
        }
    }
}
xhr.send(null)

test.json

{
    "name": "zhangsan"
}

运行结果

流程和api说明

get请求的send方法可以传null也可以不传,我估摸着源码判断就是 == null,这样同时判断undefined和null

GET 还是 POST?

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。

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

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

如需使用同步 async=false,将 open() 方法中的第三个参数改为 false,不推荐使用 async=false,但是对于一些小型的请求,也是可以的。总之,都写为true是没问题的。注释:当使用 async=false 时,不要编写 onreadystatechange 函数 - 把准备写的逻辑放到 send() 语句后面即可(但没有谁会这样用)

xhr.status就是http协议的状态,如下图

当请求被发送到服务器时,我们需要执行一些基于响应的任务。每当 readyState 改变时,就会触发 onreadystatechange 事件(这是一个回调函数)。readyState 属性存有 XMLHttpRequest 的状态信息。

xhr.readyState会从0到4变化

0 —— (未初始化)还没有调用send()方法

1 —— (载入)已调用send()方法,服务器连接已建立,正在发送请求

2 —— (载入完成) send()方法执行完成,已经接收到全部响应内容

3 —— (交互)正在解析响应内容

4 —— (完成)响应内容解析完成,可以在客户端调用

说这个xhr.readyState会从0到4变化有啥用?这就涉及一个调试技巧,如果发现onreadystatechange回调里面的逻辑没执行,那就去onreadystatechange里面写上console.log(xhr.readyState),会经历01234这5种情况,但是打印只会2 3 4的情况,因为1才与服务器刚建立接连,请求没发送完。如果没打印就对应看问题,单词拼错了?send忘了写??

post请求

给一个示范

const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            // console.log(JSON.parse(xhr.responseText))
            alert(xhr.responseText)
        } else {
            console.log('其他情况')
        }
    }
}
const postData = {
    username: 'zhangsan',
    password: 'xxx'
}
xhr.send(JSON.stringify(postData))

2.同源策略和跨域

同源策略

什么是同源策略?

ajax请求时,浏览器要求和当前网页的server必须同源(安全)

什么是同源?

同源:协议、域名、端口,三者必须一致

比如前端http://a.com:8080/

server是https://b.com/api/xxx

比如这个前端网页上发ajax去请求server页面的数据,浏览器肯定会截获,不符合同源,不管是get还是post都不会让你发

服务端server不一样,没有同源策略,可以去获取其他的数据

可以不遵守同源策略的情况:

加载图片img、css、js可以无视同源策略

<img src=跨域的图片地址>
<link href=跨域的css地址>
<script src=跨域的js地址></script>

注意了,别人的图片可能做防盗链,是服务器做的,比如微信公众号文章的图做了防盗链的限制,如果不是自己域名的网站引用,可能说“此图片来自微信公众平台,未经允许不可引用”

比如这个,你把微信公众号文章的图复制粘贴在另一个论坛之类的,就会这样

<img />可用于统计打点,可使用第三方统计服务

<link /> <script>可使用CDN,CDN一般都是外域

<script>可实现JSONP

跨域

所有的跨域都必须经过server端允许、server端配合,未经server端允许就实现跨域,说明浏览器有漏洞,这是危险信号

3.jsonp和cors

引出问题

访问https://imooc.com/,服务端一定返回一个html文件吗?

不是,如果这样就是静态文件了,不会动态更新,服务器可以任意动态拼接数据返回,只要符合html格式要求。

同理:<script src="http://imooc.com/getData.js">就一定返回js文件吗?

不一定,都是url网址,不管是啥,服务器都会拼接任何满足条件的数据返回给你。这里服务器只需要拼接的东西符合js格式不报错就可以了。

jsonp的基本实现原理

我们知道,<script>可以绕过跨域限制,服务器可以任意动态拼接数据返回,所以<script>就可以获得跨域的数据,只要服务端愿意返回。jsonp就是这个原理。

来个代码演示:

jsonp.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>jsonp 演示</title>
    </head>
    <body>
        <p>一段文字 1</p>

        <script>
            window.abc = function (data) {
                console.log(data)
            }
        </script>
        <!-- 实际操作可以写函数把下面的script动态插入 -->
        <script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
    </body>
</html>

<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc">

这里本地演示的时候换8002端口只是为了演示跨域,运行这个html的时候在8001端口

jsonp.js(假设这里是服务器动态拼接的数据name:'xxx',意思是执行abc函数,传参{ name : 'xxx' })

abc(
    { name: 'xxx' }
)

运行结果:

注意,这里必须在jsonp.js的目录开启8002端口的服务器,否则net::ERR_CONNECTION_REFUSED,如果开启服务器位置不对就会404

你真的了解jsonp的请求过程吗(例子均为gif演示)

实际操作没必要定义window.abc=....也没必要引入<script>,只要服务端配合,前端可以定义函数或者工具封装起来来实现跨域的过程

jQuery演示jsonp

 当然,我再举个例子吧,我自己用代码试了一下,来看看jQuery的ajax请求例子

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
  </head>
  <body>
    <script>
    $(function(){
        $.ajax({
            url:'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1', 
            dataType: 'jsonp',
            crossDomain:true, // 不写这个无法跨域
            jsonCallback: 'abc', // 服务端一定会执行回调callback,但是我这里想重命名,但是有用吗??????????
            success: function(data) { // 这里就是要执行的回调函数
                console.log(data)
            }
        })
    });
    </script>
  </body>
</html>

运行结果看看

只要在url中没手动写callback=xxxx,那就算写了jsonCallback也没用,那如果是jsonp参数呢,写jsonp:'xxxx'参数就相当于在url手动加上xxxx=?没手动写callback=?后面还是随机的

$.ajax({
    url:'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1',
    dataType: 'jsonp',
    crossDomain:true,
    jsonp: 'xxxx', // 新加的属性
    jsonCallback: 'abc',
    success: function(data) {
        console.log(data)
    }
})

如果写了jsonp参数呢?我们来看看

其实完全可以不写jsonCallback,只需要知道一定会执行对应回调就行了

看这里,我手动加了callback=1233,没用的大兄弟,我们来看看

$.ajax({
    url:'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1&callback=1233', //让服务端将callback改名为abc
    dataType: 'jsonp',
    crossDomain:true,
    success: function(data) { // 这里就是回调的abc函数
        console.log(data)
    }
})

运行结果,可以发现2个callback,后面的读取随机callback会覆盖前面我们设置的1233,所以服务器最后返回的还是浏览器随机产生的,jQuery里面就是这样,但是vue的jsonp可就不一样了,写啥就是啥,后面我们来看看vue的jsonp

vue演示jsonp

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://unpkg.com/vue"></script>
    <!-- 注意:vue-resource 依赖于 Vue,所以先后顺序要注意  -->
    <!-- this.$http.jsonp -->
    <script src="https://unpkg.com/vue-resource"></script>
  </head>
  <body>
    <div id="app">
      <input type="button" value="jsonp请求" @click="jsonpInfo">
    </div>
    <script>
      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
          jsonpInfo() { // 发起JSONP 请求
            this.$http.jsonp('http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1',
			{jsonpCallback:"lcytestname"}
			).then(result => {
              console.log(result.body)
            })
          }
        }
      });
    </script>
  </body>
</html>

一般可以这样设置

this.$http.jsonp(url,{params:params,jsonp: 'callBackParam',jsonpCallback: "getCallBack"})
jsonp设置的是传callback的参数名,就是jsonp:xxx,则到时候请求就是xxx=?而不是callback=?

jsonpCallback是返回的函数名

vue是自动处理跨域,这完全没问题,不用另外设置什么crossDomain属性

运行结果

上面例子我都测试过了,大家可以直接复制自行测试学习!找例子不容易~~~

cors

服务器设置http header,大家感兴趣可以自行搜索

4.练习题

1.手写一个简易的ajax

简单原理如下图,接着我们写一下完整点的代码

function ajax(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(JSON.parse(xhr.responseText));
                } else if (xhr.status === 404) { // 如果需要更多情况可自行添加判断
                    reject(new Error("404 not found"));
                }
            }
        };
        xhr.send(null)
    })
    return p;
}
const url = '/data/test.json'
ajax(url).then(res => console.log(res))
.catch(err => console.log(err));

更多的却不一定让你去手写一个ajax,因为ajax已经比较完善了,手写这些东西是为了了解原理,框架都是封装,原理类似,axios也是利用XMLHttpRequest封装的

关注、留言,我们一起学习。

===============Talk is cheap, show me the code================

发布了224 篇原创文章 · 获赞 983 · 访问量 91万+

猜你喜欢

转载自blog.csdn.net/qq_34115899/article/details/104145304
今日推荐