全面分析前端的网络请求方式

站长手记 作者: 2024-08-28 03:00:01
大多数情况下,在前端发起一个网络请求我们只需关注下面几点:传入基本参数(url,请求方式),请求参数、请求参数类型,设置请求获取响应的方式

一、前端进行网络请求的关注点

  • 传入基本参数(url,请求方式)
  • 请求参数、请求参数类型
  • 设置请求头
  • 获取响应的方式
  • 获取响应头、响应状态、响应结果
  • 异常处理
  • 携带cookie设置
  • 跨域请求

二、前端进行网络请求的方式

  • form表单、ifream、刷新页面
  • Ajax - 异步网络请求的开山鼻祖
  • jQuery - 一个时代
  • fetch - Ajax的替代者
  • axios、request等众多开源库

三、关于网络请求的疑问

  • Ajax的出现解决了什么问题
  • 原生Ajax如何使用
  • jQuery的网络请求方式
  • fetch的用法以及坑点
  • 如何正确的使用fetch
  • 如何选择合适的跨域方式

四、Ajax的出现解决了什么问题

五、原生Ajax的用法

        var xhr = new XMLHttpRequest();
        xhr.open('post','www.xxx.com',true)
        // 接收返回值
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 ){
                if(xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                    console.log(xhr.responseText);
                }
            }
        }
        // 处理请求参数
        postData = {"name1":"value1","name2":"value2"};
        postData = (function(value){
        var dataString = "";
        for(var key in value){
             dataString += key+"="+value[key]+"&";
        };
          return dataString;
        }(postData));
        // 设置请求头
        xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        // 异常处理
        xhr.onerror = function() {
           console.log('Network request failed')
        }
        // 跨域携带cookie
        xhr.withCredentials = true;
        // 发出请求
        xhr.send(postData);

函数

xhr.open(method, url, async);
  • method:请求方式,如get、post
  • url:请求的url
  • async:是否为异步请求
xhr.send(param)
  • param:http请求的参数,可以为string、Blob等类型。
xhr.abort()
xhr.setRequestHeader(header, value);
var header = xhr.getResponseHeader(name);

属性

状态 描述
0 UNSENT 代理被创建,但尚未调用 open() 方法。
1 OPENED open() 方法已经被调用。
2 HEADERS_RECEIVED send() 方法已经被调用,并且头部和状态已经可获得。
3 LOADING 下载中; responseText 属性已经包含部分数据。
4 DONE 下载操作已完成。
描述
"" 将 responseType 设为空字符串与设置为"text"相同, 是默认类型 (实际上是 DOMString)。
"arraybuffer" response 是一个包含二进制数据的 JavaScript ArrayBuffer 。
"blob" response 是一个包含二进制数据的 Blob 对象 。
"document" response 是一个 HTML Document 或 XML XMLDocument ,这取决于接收到的数据的 MIME 类型。
"json" response 是一个 JavaScript 对象。这个对象是通过将接收到的数据类型视为 JSON 解析得到的。
"text" response 是包含在 DOMString 对象中的文本。

事件回调

xhr.onreadystatechange = callback;
xhr.onloadstart = callback;
xhr.onprogress = function(event){
  console.log(event.loaded / event.total);
}
xhr.onload = callback;

异常处理

xhr.onerror = callback;
xhr.ontimeout = callback;

六、jQuery对Ajax的封装

$.ajax({
    dataType: 'json', // 设置返回值类型
    contentType: 'application/json', // 设置参数类型
    headers: {'Content-Type','application/json'},// 设置请求头
    xhrFields: { withCredentials: true }, // 跨域携带cookie
    data: JSON.stringify({a: [{b:1, a:1}]}), // 传递参数
    error:function(xhr,status){  // 错误处理
       console.log(xhr,status);
    },
    success: function (data,status) {  // 获取结果
       console.log(data,status);
    }
})
isSuccess = status >= 200 && status < 300 || status === 304;
"xml": 返回 XML 文档,可用 jQuery 处理。
"html": 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。
"script": 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 "cache" 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载)
"json": 返回 JSON 数据 。
"jsonp": JSONP 格式。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。
"text": 返回纯文本字符串
{
    a: 1,
    b: 2,
    c: 3
}
{
  data: {
    a: [{
      x: 2
    }]
  }
}
$.ajax({
    dataType: 'json',
    contentType: 'application/json',
    data: JSON.stringify({a: [{b:1, a:1}]})
})

七、jQuery的替代者

这种功能以前是使用 XMLHttpRequest实现的。Fetch提供了一个更好的替代方法,可以很容易地被其他技术使用,例如 Service Workers。Fetch还提供了单个逻辑位置来定义其他HTTP相关概念,例如CORS和HTTP的扩展。

八、fetch的使用

const options = {
    method: "POST", // 请求参数
    headers: { "Content-Type": "application/json"}, // 设置请求头
    body: JSON.stringify({name:'123'}), // 请求参数
    credentials: "same-origin", // cookie设置
    mode: "cors", // 跨域
}
fetch('http://www.xxx.com',options)
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson); // 响应数据
  })
  .catch(function(err){
    console.log(err); // 异常处理
  })
  • fetch()
  • Headers
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
  • Request
  • Response

九、fetch polyfill源码分析

fetch 封装

  • 构造一个Promise对象并返回
  • 创建一个Request对象
  • 创建一个XMLHttpRequest对象
  • 取出Request对象中的请求url,请求方发,open一个xhr请求,并将Request对象中存储的headers取出赋给xhr
  • xhr onload后取出response的status、headers、body封装Response对象,调用resolve。

异常处理

  • 1.请求超时
  • 2.请求失败
  • 3.手动终止
这是一个实验中的功能,此功能某些浏览器尚在开发中


Headers封装

Request对象

  • 传入的headers被当作Headers构造函数的参数来构造header对象。

cookie处理

    if (request.credentials === 'include') {
      xhr.withCredentials = true
    } else if (request.credentials === 'omit') {
      xhr.withCredentials = false
    }
mdn: 默认情况下,fetch 不会从服务端发送或接收任何 cookies

Response对象

    xhr.onload = function () {
      var options = {
        status: xhr.status,
        statusText: xhr.statusText,
        headers: parseHeaders(xhr.getAllResponseHeaders() || '')
      }
      options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
      var body = 'response' in xhr ? xhr.response : xhr.responseText
      resolve(new Response(body, options))
    }
    var rejected = consumed(this)
    if (rejected) {
      return rejected
    }
function consumed(body) {
  if (body.bodyUsed) {
    return Promise.reject(new TypeError('Already read'))
  }
  body.bodyUsed = true
}
因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次

十、fetch的坑点

使用fetch还有很多别的注意事项,这也是为什么大家现阶段还是更喜欢 axios 多一些。当然这个事情在未来可能会发生改变。
  • 不能直接传递JavaScript对象作为参数
  • 需要自己判断返回值类型,并执行响应获取返回值的方法
  • 获取返回值方法只能调用一次,不能多次调用
  • 无法正常的捕获异常
  • 老版浏览器不会默认携带cookie
  • 不支持jsonp

十一、对fetch的封装

请求参数处理

function stringify(url, data) {
  var dataString = url.indexOf('?') == -1 ? '?' : '&';
  for (var key in data) {
    dataString += key + '=' + data[key] + '&';
  };
  return dataString;
}

if (request.formData) {
  request.body = request.data;
} else if (/^get$/i.test(request.method)) {
  request.url = `${request.url}${stringify(request.url, request.data)}`;
} else if (request.form) {
  request.headers.set('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
  request.body = stringify(request.data);
} else {
  request.headers.set('Content-Type', 'application/json;charset=UTF-8');
  request.body = JSON.stringify(request.data);
}

cookie携带

  request.credentials =  'same-origin'; // 同源携带
  request.credentials =  'include'; // 可跨域携带

异常处理

当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
.then(response => {
  if (response.ok) {
    return Promise.resolve(response);
  }else{
    const error = new Error(`请求失败! 状态码: ${response.status}, 失败信息: ${response.statusText}`);
    error.response = response;
    return Promise.reject(error);
  }
});

返回值处理

.then(response => {
  let contentType = response.headers.get('content-type');
  if (contentType.includes('application/json')) {
    return response.json();
  } else {
    return response.text();
  }
});

jsonp

十二、跨域总结

nginx

cors

app.all('*', function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    next();
});

jsonp

        $.ajax({
            type : "get",
            url : "http://xxxx"
            dataType: "jsonp",
            jsonp:"callback", 
            jsonpCallback: "doo",
            success : function(data) {
                console.log(data);
            }
        });
(function (window,document) {
    "use strict";
    var jsonp = function (url,data,callback) {

        // 1.将传入的data数据转化为url字符串形式
        // {id:1,name:'jack'} => id=1&name=jack
        var dataString = url.indexof('?') == -1? '?': '&';
        for(var key in data){
            dataString += key + '=' + data[key] + '&';
        };

        // 2 处理url中的回调函数
        // cbFuncName回调函数的名字 :my_json_cb_名字的前缀 + 随机数(把小数点去掉)
        var cbFuncName = 'my_json_cb_' + Math.random().toString().replace('.','');
        dataString += 'callback=' + cbFuncName;

        // 3.创建一个script标签并插入到页面中
        var scriptEle = document.createElement('script');
        scriptEle.src = url + dataString;

        // 4.挂载回调函数
        window[cbFuncName] = function (data) {
            callback(data);
            // 处理完回调函数的数据之后,删除jsonp的script标签
            document.body.removeChild(scriptEle);
        }

        document.body.appendChild(scriptEle);
    }

    window.$jsonp = jsonp;

})(window,document)

postMessage跨域

//捕获iframe
var domain = 'http://scriptandstyle.com';
var iframe = document.getElementById('myIFrame').contentWindow;

//发送消息
setInterval(function(){
    var message = 'Hello!  The time is: ' + (new Date().getTime());
    console.log('blog.local:  sending message:  ' + message);
        //send the message and target URI
    iframe.postMessage(message,domain); 
},6000);
//响应事件
window.addEventListener('message',function(event) {
    if(event.origin !== 'http://davidwalsh.name') return;
    console.log('message received:  ' + event.data,event);
    event.source.postMessage('holla back youngin!',event.origin);
},false);

WebSocket

         function WebSocketTest(){
            if ("WebSocket" in window){
               alert("您的浏览器支持 WebSocket!");
               // 打开一个 web socket
               var ws = new WebSocket("ws://localhost:3000/abcd");
               ws.onopen = function(){
                  // Web Socket 已连接上,使用 send() 方法发送数据
                  ws.send("发送数据");
                  alert("数据发送中...");
               };
               ws.onmessage = function (evt) { 
                  var received_msg = evt.data;
                  alert("数据已接收...");
               };
               ws.onclose = function(){ 
                  // 关闭 websocket
                  alert("连接已关闭..."); 
               };
            } else{
               // 浏览器不支持 WebSocket
               alert("您的浏览器不支持 WebSocket!");
            }
         }
原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_69894.html
前端 前端网络请求