重生的 Ajax ,fetch 的降临

本来这篇文章是和 Promise 一起写的,后来学 fetch POST 请求怎么都没理解就单独把这篇文章拿出来自立一篇。传统的 Ajax (小黄人)请求因为设计比较反人类,所以很难用,为了弥(mi 我一直以为是 ni)补,这个不太受待见的东西, fetch 出现了,甚至 fetch 的好用都超过了 JQuery 的封装好的 Ajax 请求。一起来学学。

一、小黄人(XHR)

复习一下小黄人的 GET 和 POST 请求

  • get 请求
  //垃圾的小黄人
   const xhr = new XMLHttpRequest();
   xhr.onreadystatechange = function(){
        if(this.readyState == 4){
            if(this.status ==200 || this.status.toString().indexOf(3) == 0){
               const text = this.responseText;
               console.log(text);
            }
        }
   }
   xhr.open("get","6.txt",true);
   xhr.send();
  • post 请求
 //垃圾的小黄人
   const xhr = new XMLHttpRequest();
   xhr.onreadystatechange = function(){
        if(this.readyState == 4){
            if(this.status ==200 || this.status.toString().indexOf(3) == 0){
                var a = xhr.responseText;
                console.log(a);
            }
        }
   }
   xhr.open("post","1.php",true);
   // post表格提交必须加上这一条
   xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
   xhr.send("name=hero");
//=======================
<?php
    $a = $_POST["name"];
    echo "成功!";
?>

看见上面代码了吧,是不是很不爽的感觉。一个请求居然要写那么多的内容。小黄人在设计上不符合职责分离原则,将输入、输出和用事件来跟踪的状态混杂在一个对象里。而且,基于事件的模型与 JavaScript 流行的 Promise 以及基于生成器的异步编程模型不太搭,事件模型在处理异步上有点过时了。

轮到 fetch 登场了,先来看看 fetch 的基本语法:
fetch(input, init).then(function(response) { ... });
参数:

  1. input
    定义要获取的资源一般是个 URL 地址。
  2. init 可选 一个配置项对象,包括所有对请求的设置。可选的参数有:
  • method: 请求使用的方法,如 GET、POST,默认GET。
  • headers: 请头信息,可以是简单的对象,也可以是 Headers 的实例;
  • body: 请求的携带的 body 信息格式是字符串的JSON:类似'{"name":"Condor Hero","age":18,"sex":"girl"}'或JSON.stringify({"name":"hero","age":18,"sex":"girl"})
  • 注意 GET 或 HEAD 方法的请求不能包含 body 信息。
  • mode: 请求的模式,如 cors(跨域)、 no-cors:只允许使用 GET 、 HEAD 、 POST ; same-origin:同源请求;
  • credentials: 是否发送 cookies,如omit:不发送,默认、same-origin:同源发送;或者 include:发送。
  • cache 缓存策略: 请求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached.

html5 引进这个新的 Fetch API ,本质是天生返回 Promise 对象,配合 async/await 语法使用。

二、fetch 的默认 GET 请求

来看看最新版的原生 Ajax 请求。一句话搞定了。
fetch("6.txt").then(res => res.text()).then(res => console.log(res)).catch(err => console.log(err));

鹧鸪天·手捻香笺忆小莲

请求类型也是一种新的 fetch 请求

我们来研究一下第一个 then 里面的返回值,打印出来如下图:
fetch("http://127.0.0.1/2/1.txt").then(res =>console.log(res));

讲讲怎么用

  • 如果请求的是 json 数据,需要调用 res.json(),将可读流解析为 json 数据,在下一个then 方法中,就可以得到想要的 json 数据了。同理,如果请求的是 txt 文本数据,则需要调用 res.text() 来解析。
  • 第二个 then 里面的 res 用来接收,后台返回的结果。
三、fetch 的 POST 请求
fetch("http://127.0.0.1/2/",{method:"post"}).then(res=>res.json()).then(res=>console.log(res));

必须在http://127.0.0.1/line.html 打开请求,不然提示没有请求头,即使设置:mode:"core" 这个属性是表示服务器设置了core 的才会响应设置这个的 POST 请求。
Post 请求我测试请求路径只能写到文件夹,文件夹里面打开文件的优先级是 index.html →index.php 等其他语言的 index。

2019 年 7 月 19 日也就是昨天没解决的一个问题?

与 php 后台交互,注意看下图的请求方式 Request Payload 不是 Form Data,至于两者的区别参见下方的博客,Request Payload 的请求 PHP 通过 $_POST[] 方法拿不到数据,百度一圈,说可以用print_r(file_get_contents("php://input")); 结果还是拿到了寂寞,我也不是太懂 PHP,有知道的小伙伴欢迎留言。这个问题就留这里了,有机会以后再来解决,谁让 PHP 学的不好。

HTTP请求中的Form Data 与 Request Payload 的区别 - 掘金

var data = {"name":"Condor Hero","age":18,"sex":"girl"};
fetch("./2",{
        mode:"cors",
        //  mode - 是否允许跨域请求,以及哪些响应的属性是可读的。
        //  可选值:cors:(默认, 允许跨域请求,将遵守 CORS 协议)
        //  no-cors:该模式允许来自 CDN 的脚本、其他域的图片和其他一些跨域资源。
        //  前提条件是请求的 method 只能是 HEAD、GET 或 POST。而且 js 不能访问 Response 对象中的任何属性
        //  same-origin:要同源,不允许跨域。
        method:"POST",
        headers: {
            'Content-Type': 'application/json'
        },
        //json字符串和对象都可以,推荐使用json字符串,这样可以在控制台network里看到请求参数
        body: JSON.stringify(data)
        // 请求参数
        }).then(res =>res.json()).then(res=>console.log(res));
fetch post

既然我是用本地 PHP 模拟的环境没搞明白,那就用 node.js 或者直接上网趴个接口不就完了,我个沙雕。现在 POST 请求用一个网址返回的内容是一个数组,数组里面是一个大 JSON。


使用大概结果如图

现在来发送一个经典的 POST 请求模板,并得到结果。

fetch("https://jsonplaceholder.typicode.com/users/",{
    method : "POST",
    headers : {
        "Content-Type" : "application/json"
    },
    body:JSON.stringify({
        "name":"Condor Hero",
        "age":18,
        "sex":"girl"
    })
    //也可以这样写'{"name":"Condor Hero","age":18,"sex":"girl"}'
}).then(res=>res.json()).then(res=>console.log(res));
POST请求结果

看见上图发送 POST 请求的 OPTIONS 了吧,我们接下来揭示一下他的原理。

四、 跨域策略

参考:AJAX - 廖雪峰的官方网站

  • JSONP跨域
    我们在使用最原始的 Ajax 小黄人请求数据时,受同源策略无法跨域访问文件,不管请求的是什么文件,只要是跨域请求,一律不准。后来发现 script 标签的 src 则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比<img>、<iframe>) 于是诞生了 JOSNP;京东商品评价就是用 JSONP 写的,我们来模拟一下;
<script>
    function handleComboCallback(data){
        console.log(data);
    }
</script>
<script src = "https://c.3.cn/recommend?callback=handleComboCallback&methods=accessories&p=103003&sku=7299682&cat=670%2C677%2C688&lid=1&uuid=1557651031719828760805&pin=&ck=pin%2CipLocation%2Catw%2Caview&lim=5&cuuid=1557651031719828760805&csid=122270672.2.1557651031719828760805%7C4.1563593620&_=1563593637540"></script>
JSONP 请求
  • CORS 跨域
    现在我们有一劳永逸新的跨域策略:CORS了。CORS 全称 Cross-Origin Resource Sharing,是 HTML5 规范定义的如何跨域访问资源。Origin 表示本域,也就是浏览器当前页面的域。当J avaScript 向外域(如sina.com)发起请求后,浏览器收到响应后,首先检查Access-Control-Allow-Origin是否包含本域,如果是,则此次跨域请求成功,如果不是,则请求失败,JavaScript 将无法获取到响应的任何数据。

跨域能否成功,取决于对方服务器是否愿意给你设置一个正确的 Access-Control-Allow-Origin,决定权始终在对方手中。

上面这种跨域请求,称之为“简单请求”。简单请求包括GET 和 POST(主要包括 POST
的 Content-Type 类型中的 application/x-www-form-urlencoded、multipart/form-data和text/plain),并且不能出现任何自定义头(例如,X-Custom: 12345),通常能满足90%的需求。

重点:对于 application/json 的 POST 请求,在发送 AJAX 请求之前,浏览器会先发送一个OPTIONS 请求(称为 preflighted 请求)到这个 URL 上,询问目标服务器是否接受请求。

OPTIONS

服务器必须响应并明确指出允许的 Method 请求,access-control-allow-credentials:true
所以浏览器必须先确认服务器响应的 Access-Control-Allow-Methods 头确实包含将要发送的 AJAX 请求的 Method,才会继续发送 AJAX,否则,抛出一个错误。

五、fetch 结合async 和 await

Fetch API 是基于 Promise 设计的,所以结合 async 和 await 可以这样写。

async function main(){
    var data1 = await fetch("./1.txt").then(data=>data.text());
    var data2 = await fetch("./2.txt").then(data=>data.text());
    var data3 = await fetch("./3.txt").then(data=>data.text());

    console.log(data1);
    console.log(data2);
    console.log(data3);
}
main();

Fetch 常见坑:

  • Fetch 请求默认是不带 cookie 的,需要设置 fetch(url, {credentials: 'include'})
  • 服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject.。

附上一个参考博客:Fetch进阶指南

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容