腾讯IVWEB团队:前端 fetch 通信
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~
作者:villainthr
文章摘自: 前端小吉米
随着前端异步的发展, XHR 这种耦合方式的书写不利于前端异步的 Promise 回调. 而且,写起来也是很复杂. fetch API 本来是在 SW(ServiceWorkers) 中提出的, 不过, 后面觉得好用, 就把他挂载到 window 对象下. 这样, 在前端的正常通信中, 我们也可以直接调用. 但, fetch 毕竟比较新, 看一下他的兼容性.

在 PC 端上, 就 FF, Opera 和 Chrome 比较 fashion. mobile 的话, 基本上是不能用的. 当然, 前端一直是个拥抱变化的职业, 官方已经有一个现成的 polyfill 可以使用. 这样的话, 就没必要过多担心.
每用到一个新的 feature, 我们首先得知道他能不能用. Modernizr 这个库做的挺好的. 这里, 我们简单的了解一下就 ok 了.
let isFetch = window.fetch?true:false;
fetch 基本格式
可以说, fetch 就是 ajax + Promise. 使用的方式和 jquery 提供的 $.ajax() 差不多.
fetch('./api/some.json')
.then(
function(response) {
if (response.status !== ) {
console.log(`返回的响应码${response.status}`);
return;
}
// 获得后台实际返回的内容
response.json().then(function(data) {
console.log(data);
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
上面的 demo 很好的参数了, fetch 的几个特点.
- then()
- catch()
- json()
then 和 catch 是 promise 自带的两个方法, 我这里就不多说了. 我们来看一下,json 是干嘛的.
因为返回回来的数据不仅仅包括后台的返回的 Text 内容, 还有一些 Headers. 所以在, then 方法里面返回来的 res 实际上并不是我们在业务中想要的内容. 就和在 XHR 里返回数据是一个道理, 我们最终要的是 responseText 这个数据. 而 json() 方法实际做的事情,就是调用 JSON.parse() 处理数据, 并且返回一个新的 Promise. 看一下 polyfill 源码就应该了解.
this.json = function() {
return this.text().then(JSON.parse)
}
这里需要注意的是,fetch 中的 response/request 都是 stream 对象。
fetch 传输格式
上面的 demo 是一个 get 方法的请求, 当然, 除了 get , 还有其他的 HTTP Method, PUT, DELETE, POST, PATCH 等. 这里, 我们就说一个 POST, 其他方法的基本格式还是类似的.
fetch("http://www.example.org/submit.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "this is a post Msg"
}).then(function(res) {
if (res.ok) {
// doSth
} else if (res.status == ) {
// doSth
}
});
看起来 fetch 和 $.ajax 并没有多大的区别...
but... fetch 里面的内容,真不少. 往底层看一看, fetch 实际上是 Request,Headers,Response 3个接口的整合. 不过, 这3个只能在 SW 里面使用. 这里当做原理,参数一下即可.
Headers 操作
Headers 的操作无非就是 CRUD, 这里我就不过多赘述,直接看代码吧:
var content = "Hello World";
var reqHeaders = new Headers();
reqHeaders.append("Content-Type", "text/plain"
reqHeaders.append("Content-Length", content.length.toString());
reqHeaders.append("X-Custom-Header", "自定义头");
当然, 你也可以使用字面量的形式:
reqHeaders = new Headers({
"Content-Type": "text/plain",
"Content-Length": content.length.toString(),
"X-Custom-Header": "自定义头",
});
接下来就是, 头部的内容的检测相关.
console.log(reqHeaders.has("Content-Type")); // true
console.log(reqHeaders.has("Set-Cookie")); // false
reqHeaders.set("Content-Type", "text/html");
reqHeaders.append("X-Custom-Header", "新增自定义头");
console.log(reqHeaders.get("Content-Length")); //
console.log(reqHeaders.getAll("X-Custom-Header")); // ["自定义头", "新增自定义头"]
reqHeaders.delete("X-Custom-Header");
console.log(reqHeaders.getAll("X-Custom-Header")); // []
不过, 鉴于安全性的考虑, 有时候在多人协作或者子版块管理时, 对于头部的限制还是需要的. 这里, 我们可以通过 guard 属性, 设置 Headers 的相关策略.
guard 通常可以取 "immutable", "request", "request-no-cors", "response", "none".
我们这里不探讨全部, 仅仅看一下 request 这个选项.
当你设置了 request 之后, 如果你设置的 Header 涉及到 forbidden header name (这个是浏览器自动设置的), 那么该次操作是不会成功的.
forbidden header name 通常有.
- Accept-Charset
- Accept-Encoding
- Access-Control-Request-Headers
- Access-Control-Request-Method
- Connection
- Content-Length
- Cookie
- Cookie2
- Date
- DNT
- Expect
- Host
- Keep-Alive
- Origin
- Referer
- TE
- Trailer
- Transfer-Encoding
- Upgrade
- Via
对比与 fetch, 我们没有办法去设置 Headers的 guard, 所以, 这只能在 SW 里使用.
Request 操作
Request 的基本用法和 fetch 差不多.
var uploadReq = new Request("/uploadImage", {
method: "POST",
headers: {
"Content-Type": "image/png",
},
body: "image data"
});
fetch("/uploadImage", {
method: "POST",
headers: {
"Content-Type": "image/png",
},
body: "image data"
});
那 fetch 有什么用呢?
关键的地方在于,fetch 实际上就是 request/reponse 的容器,request/response 相当于就是两个元数据,fetch 只是实际进行的操作。所以,为了达到更高的复用性,我们可以 ajax 的请求,实例化为一个个具体的对象。
var getName = new Request(...,{//...});
var getGender = new Request(...,{//...});
// 发送请求
fetch(getName)
.then((res)=>{});
fetch(getGender)
.then((res)=>{});
在浏览器里, 一切请求都逃不过跨域和不跨域的问题. fetch 也是. 对于跨域的请求, 主要的影响还是体现在 Response 中, 这 fetch Request 这, 没多大影响. 不过, 我们需要在 fetch 设置 mode 属性, 来表示这是一个跨域的请求.
fetch('https://www.villainhr.com/cors-enabled/some.json', {mode: 'cors'})
.then(function(response) {
return response.text();
})
常用的 mode 属性值有:
- same-origin: 表示只请求同域. 如果你在该 mode 下进行的是跨域的请求的话, 那么就会报错.
- no-cors: 正常的网络请求, 主要应对于没有后台没有设置
Access-Control-Allow-Origin. 话句话说, 就是用来处理 script, image 等的请求的. 他是 mode 的默认值. - cors: 用来发送跨域的请求. 在发送请求时, 需要带上.
- cors-with-forced-preflight: 这是专门针对 xhr2 支持出来的 preflight,会事先多发一次请求给 server,检查该次请求的合法性。
另外, 还有一个关于 cookie 的跨域内容. 在 XHR2 中,我们了解到, withCredentials 这个属性就是用来设置在进行跨域操作时, 对不同域的 Server 是否发送本域的 cookie. 一般设置为 omit(不发送). 在 fetch 当中, 使用的是 credentials 属性.
credentials 常用取值为:
- omit: 发送请求时,不带上 cookie. 默认值.
- same-origin: 发送同域请求时,会带上 cookie.
- include: 只要发送请求,都会带上 cookie.
所以, 如果你想发送 ajax 时, 带上 cookie, 那么你就需要使用 same-origin, 如果想在跨域时也带上 cookie, 那么就需要 include.
// 跨域请求
fetch('https://www.villainhr.com/cors-enabled/some.json', {mode: 'cors',credentials:'include'})
.then(function(response) {
return response.text();
})
Response 操作
response 应该算和 fetch 最为接近的一个对象. Response 的实际其实就是 fetch 回调函数传回的参数. Response 中比较常用的属性有四个: status, statusText, ok, type.
- status: 返回的状态码. 100~500+
- statusText: 返回状态码代表的含义. 比如, 返回"ok".
- ok: 用来检差 status 是否在200和299之间.
- type: 表示请求是否跨域, 或是否出错. 取值为: “basic”, “cors”, “default”, “error” 或
“opaque”.
fetch('https://www.villainhr.com/cors-enabled/some.json', {mode: 'cors',credentials:'include'})
.then(function(response) {
// ...
})
这里, 我们主要关心一下 type 上面挂载的一些属性.
- basic: 同域通信类别. 可以正常的访问 response 的 header(除了 Set-Cookie 头).
- cors: 跨域通信类别. 一般只能访问以下的头:
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
- error: 网络错误类别.
- opaque: 无法理解类别. 当使用
no-cors发送跨域请求时,会触发.
另外,在 response 上面,还挂载了几个常用的方法: text(),json().
- text(): 主要用来处理 server 返回的 string 类型数据.
- josn(): 主要用来处理 server 返回的 json 类型数据.
使用方式都是流式 API.
fetch('https://www.villainhr.com/cors-enabled/some.json')
.then(function(res) {
res.text().then((text)=>{...})
res.json().then((obj)=>{...})
})
body 处理
我们通过 ajax 请求数据时,可能会收到,ArrayBuffer,Blob/File,string,FormData 等等。并且,在发送的时候比如:
var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
method: "POST",
body: form
})
fetch 会自动设置相关的 Content-Type 的头。另外,如果我们可以手动生成一个响应流(方便后面其他操作)。
var res = new Response(new File(["chunk", "chunk"], "archive.zip",{ type: "application/zip" }));
流的处理
因为,req/res 都是以流的形式存在的,即,req/res 的 body 只能被使用一次。相当于就是一个文件从缓存读到硬盘里面,那么原来文件就已经消失了。我们可以通过 bodyUsed 去检查,该对象是否已经被使用。
var res = new Response("one time use");
console.log(res.bodyUsed); // false
res.text().then(function(v) {
console.log(res.bodyUsed); // true
});
console.log(res.bodyUsed); // true
res.text().catch(function(e) {
console.log("Tried to read already consumed Response");
});
这样做的原因主要是为了让以后 Web 更好的处理视频的相关数据。那如果我有时候想要使用多次,那该怎么办?
例如,我们 Service Worker 中,使用 caches API 缓存响应,然后后面我还要将该响应返回给浏览器,那么这里 response 流就被使用了两次。这里,就和普通的流操作一样,将该流克隆一份,使用:
addEventListener('fetch', function(evt) {
var sheep = new Response("Dolly");
console.log(sheep.bodyUsed); // false
var clone = sheep.clone();
console.log(clone.bodyUsed); // false
clone.text();
console.log(sheep.bodyUsed); // false
console.log(clone.bodyUsed); // true
evt.respondWith(cache.add(sheep.clone()).then(function(e) {
return sheep;
});
});
参考
基本的内容就是上述内容. 如果想更详细参考的话, 请查阅:
【有奖讨论】程序员,怎么应对三十岁? 点击查看详情
欢迎加入QQ群:374933367,与腾云阁原创作者们一起交流,更有机会参与技术大咖的在线分享!
相关阅读
腾讯工程师们怎么玩 Vue.js?
React 入门学习
React同构直出优化总结
此文已由作者授权腾讯云技术社区发布,转载请注明文章出处
原文链接:https://www.qcloud.com/community/article/557775001489391626
获取更多腾讯海量技术实践干货,欢迎大家前往腾讯云技术社区
腾讯IVWEB团队:前端 fetch 通信的更多相关文章
- 前端 fetch 通信
随着前端异步的发展, XHR 这种耦合方式的书写不利于前端异步的 Promise 回调. 而且,写起来也是很复杂. fetch API 本来是在 SW(ServiceWorkers) 中提出的, 不过 ...
- Springboot第三篇:与前端fetch通信(关于前端传输json数据上传文件等等前后端的处理)
关于前端接口传递的方法,推荐按以下使用: 若要在服务器上创建资源,推荐使用POST方法 若要检索某个资源,推荐使用GET方法 若要更新资源,推荐使用PUT方法 若要删除某个资源,推荐使用DELETE方 ...
- Springboot第二篇:与前端fetch通信(附springboot解决跨域方法)
说到与前端通信,明白人都知道这章肯定会写两部分的东西啦. 关于后台 ①首先回顾前文,上一章环境搭建如图: ②我们在maven.example.controller下添加一个文件,并附上如图代码: ③: ...
- 腾讯IVWEB团队:WebRTC 点对点直播
作者:villainthr 摘自:villainhr WebRTC 全称为:Web Real-Time Communication.它是为了解决 Web 端无法捕获音视频的能力,并且提供了 peer- ...
- 腾讯alloyteam团队前端代码规范
来源于:http://alloyteam.github.io/CodeGuide/ 命名规则 项目命名 全部采用小写方式, 以下划线分隔. 例:my_project_name 目录命名 参照项目命名规 ...
- 腾讯IVWEB前端工程化工具feflow思考与实践
本篇文章主要介绍腾讯IVWEB团队从0到1在工程化的思考和实践.feflow的全称是Front-end flow(前端工作流),致力于提升研发效率和规范的工程化解决方案.愿景是通过feflow,可以使 ...
- 如何开发一款堪比APP的微信小程序(腾讯内部团队分享)
一夜之间,微信小程序刷爆了行业网站和朋友圈,小程序真的能如张小龙所说让用户"即用即走"吗? 其功能能和动辄几十兆安装文件的APP相比吗? 开发小程序,是不是意味着移动应用开发的一次 ...
- 腾讯QQ团队开源分布式后台毫秒服务引擎全解析:引擎架构、RPC、灰度……
腾讯QQ团队将于12月4日开源一个服务开发运营框架,叫做毫秒服务引擎(Mass Service Engine in Cluster,MSEC),它集RPC.名字发现服务.负载均衡.业务监控.灰度发布. ...
- 腾讯bugly团队提供的android国内镜像
腾讯bugly团队提供的国内镜像 如果使用Android SDK Manager下载比较慢或者打不开,可以使用国内镜像 使用说明 http://android-mirror.bugly.qq.co ...
随机推荐
- 关于EF+MVC5分页查询数据效率问题
2017-03-31 11:57:41,290 [5] ERROR ErrorMsg - System.Data.Entity.Core.EntityCommandExecutionException ...
- Linux环境下的IDE,极大提升编程效率
"一个真正的程序员是不用IDE(译者注:集成开发环境)的,他们都是用带着某某插件的文本编辑器来写代码."我们总能在某些地方听到此类观点.然 而,尽管越来越多的人同意这样的观点,但是 ...
- 设置ssh只允许用户从指定的IP登陆
假设 我们公司的固定IP是 183.21.89.249 连接上我们自己进行管理的服务器 然后编辑ssh的配置文件默认 /etc/ssh/sshd_config 在文件最后面另起一行添加 ...
- Java并发编程:线程的基本状态
一.线程的基本状态 线程基本上有5种状态,分别是:NEW.Runnable.Running.Blocked.Dead. 1)新建状态(New) 当线程对象对创建后,即进入了新建状态,如:Thread ...
- JavaScript写一个表格排序类
依稀记得那是上个星期六的下午,我参加了网易暑期实习生招聘笔试.考得相当糟糕,编程题3个题通过了2个,简答题没做对,选择题貌似是20个题猜了6-7个,99%是挂了,唉唉唉!生活不只眼前的苟且,学习的脚步 ...
- 构造器和多态(Chapter8.3)
构造器不具有多态性(它们是static方法,只不过该static声明是隐式的),但还是非常有必要理解构造器怎样通过多态在复杂的层次结构中运作,这一理解将有助于大家避免一些令人不快的困扰. 在main中 ...
- WPF 杂谈——开篇简言。
这俩年多来笔者一直在从事关于WPF的开发.虽然不能说是专家级别的.但是对于WPF的应用还是有一定的了解.论他的灵活性决对不在WinForm之下.WPF的出现更是引发一段热议.他的何去何从更是让很多人感 ...
- LINUX ON AZURE 安全建议(全)
本文为个人原创,可以自由转载,转载请注明出处,多谢! 本文地址:http://www.cnblogs.com/taosha/p/6399554.html 1.网络与安全规划 Azure 虚拟网络 (V ...
- Mybatis(四) 高级映射,一对一,一对多,多对多映射
天气甚好,怎能不学习? 一.单向和双向 包括一对一,一对多,多对多这三种情况,但是每一种又分为单向和双向,在hibernate中我们就详细解析过这单向和双向是啥意思,在这里,在重复一遍,就拿一对多这种 ...
- netcore实践:跨平台动态加载native组件
缘起netcore框架下实现基于zmq的应用. 在.net framework时代,我们进行zmq开发由很多的选择,比较常用的有clrzmq4和NetMQ. 其中clrzmq是基于libzmq的Int ...