上一篇文章我们讨论了, 状态码 304 和与 http 缓存相关的头部, cache-control(res)expires(res)etag(res)If-None-Match(req)Last-Modified(res)If-Modified-Since(req)通过上两篇文章我们会发现, http 的响应报文和请求报文简直太重要了, 万变不离其中; 后端开发人员对 http 掌控会比前端人员控制大随着前后分离后, 后端同学更是把注意力放在后端的各种业务逻辑, 对前端只负责接口那么问题就来了, 有时候你也许会问, 为啥调试这个接口要用这种方法? 为啥传递参数要这么传? 为啥做前端验证这么做? 本将通过这几个问题出发, 讨论 http 方法和 ajax 的封装思路 (axios 为例) 当然, ajax 的封装网上代码无数, 本文不会重复早轮子, 旨在理解其为什么这么封装, 梳理和 http 方法之间的联系
讨论 http 方法之前, 先回顾下 http 的一些特性 ^-^http 在 1.0 版本之前没有持久链接这个概念, 每次请求一次就 TCP 三次握手建立一次连接, 很浪费; 所以, http1.1 增加了
Connection: keep-alive
这个字段, 请求头和响应头都有目前, 对于一般浏览器来说, 对于同一个域名, 大多数浏览器允许同时建立 6 个持久连接有时候请求很多, 6 个连接也忙不过来, 就涉及前端性能优化了 (面试题) 我们怎么发请求? ajax!(双向常用的是 webscokt)我们发请求就用到 http 方法, 本质就是告诉服务器客户端的意图, 所以这些方法也是顾名思义的
全部方法如下:
常用的是 get(查),post(增),put(改),delete(删)restful 风格; get 和 post 经常会被问到有什么区别(面试题), 知乎上有很多很回答讲的很细致, 我在这里只做个简单对比
get 传递参数使用? k=v&k1&=v2 这中直接在请求链接后边加参数值的形式, 有的时候需要使用 encodeURIComponent 函数转义一下字符
post 参数一般有三种
formData(qs 转参数),Request Payload
和上传文件用的
multipart/form-data
我们封装 ajax 其实就是参数传递的差别这些方法里边有个比较特殊的方法 options, 这个方法经常会出现在 CORS 跨域请求之前其目的是判断资源服务器支持哪些请求方法, 当然简单请求不会触发 CORS 预检
ajax 也不用我多说, 但是面试经常让手写原生 ajax 以 XMLHttpRequest 来来来一起来一遍:
- function callBack () {
- console.log(this.responseText)
- }
- let xhr = new XMLHttpRequest()
- xhr.onload = callBack
- xhr.open(method, url, true)
- xhr.send()
fetch 版
- let myHeaders = new Headers()
- let myInit = { method: 'GET',
- headers: myHeaders,
- mode: 'cors',
- cache: 'default' }
- fetch('flowers.jpg',myInit)
- .then(function(response) {
- return response.blob()
- })
- .then(function(myBlob) {
- var objectURL = URL.createObjectURL(myBlob)
- myImage.src = objectURL
- })
当然这对象和方法有很多的属性和方法, 我们也不是经常用原生请求, 处理起来比较费劲还有一定的兼容问题, 知道是那个意思就行, 学有余力查 MDN 研究即可我们使用当下最为流行的 ajax 库 axios 为例, 来封装一个功能强大的, 属于自己定制的 ajax, 看看 http 方法为这么这么封装
取一段 axios, 搭配一份 qs 模块:
- import axios from 'axios'
- import qs from 'qs'
- // 生成一个 axios 实例, 它有个一个 request 方法
- const service = axios.create({
- baseURL: process.env.MOCK_URL,// 结合 node 全局变量做 dev 和 build 分割
- timeout: 180000
- })
- /**
- * 通用 request 封装
- * @param method
- * @param url
- * @param data
- * @param config
- * @returns {Promise}
- */
- const request = (method, url, data, config = {}) => {
- const options = Object.assign({}, config, {
- url,
- method,
- data
- })
- options.headers = options.headers || {}
- return new Promise((resolve, reject) => { // 使用 promise 给返回结果套一层壳子
- service.request(options) // 实例发请求
- .then(res => {
- const data = res.data
- const status = res.status
- if (status === 200) {
- resolve(data)
- }
- resolve(data) // 这里的处理不是很严谨
- }).catch(res => {
- reject(res)
- })
- })
- }
- // 暴露外部方法
- export const ajax = {
- get(url, config) {
- return request('get', url, null, config)
- },
- delete(url, data, config) {
- return request('delete', url, data, config)
- },
- head(url, config) {
- return request('head', url, null, config)
- },
- post(url, data, config = {}) {
- if (!config.headers) {
- // 设置请求头参数
- config.headers = {
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
- }
- }
- return request('post', url, qs.stringify(data), config)
- },
- put(url, data, config = {}) {
- // 设置请求头参数
- config.headers = {
- 'Content-Type': 'application/json; charset=UTF-8'
- }
- return request('put', url, data, config)
- },
- patch(url, data, config) {
- return request('path', url, qs.stringify(data), config)
- },
- setCommonHeader(key, value) {
- service.defaults.headers.common[key] = value
- }
- }
有的时候我们需要 token, 或者 cookies 鉴权, 其实都是在请求头里边加点东西而已 axios 可以通过拦截器来操作
- service.interceptors.request.use(config =>{
- // Do something before request is sent
- if (store.getters.token) {
- config.headers['X-Token'] = ''// 让每个请求携带 token-- ['X-Token']为自定义 key 请根据实际情况自行修改
- }
- return config
- },
- error =>{
- // Do something with request error
- console.log(error) // for debug
- Promise.reject(error)
- })
有时候我们登录时间太长了要重新登录, 就是判断 response 里边的东西, 做个重定向
- service.interceptors.response.use(function(response) {
- // 根据条件重定向
- if (response.headers.loginstate === 'expired') {
- // router.push({ path: '/login' })
- }
- return response
- }, function(error) {
- return Promise.reject(error)
- })
来源: https://juejin.im/entry/5abc5a62518825558c4780e3