简介 Fetch API


XMLHttpRequest使用上不便,就算是标准化后的 XMLHttpRequest Level 1 也只是功能上的加强,开发者通常会进一步地使用程序库封装,例如〈封装 Ajax 操作〉做的那些事情。

2014 年 HTML5 正式标准化,Fetch API 是 HTML5 的一部份,Google、Mozilla 在 2015 年于浏览器开始提供实现。

从设计的角度来看,Fetch API 就像是集合了过去 Ajax 使用上一些好实践的集合体,实现了职责分离,Fetch 的工厂函数fetch可接受选项对象,而返回值是个Promise

直接来看看〈创建 XMLHttpRequest 对象〉中获取表格的范例,若改用 Fetch 的话会如何简化:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <button id='req'>获取表格</button>
    <div id="table"></div>

<script type="text/javascript">

    document.getElementById('req').onclick = function() {
        fetch('XMLHttpRequest-1.txt')
            .then(resp => resp.text())
            .then(text => document.getElementById('table').innerHTML = text);
    };

</script>   

</body>
</html>

按我观看执行结果

fetch会返回Promise,结果会是个Response实例,可以透过text方法获取承诺结果为响应文本的Promisefetch默认使用GET,若需要进一步设定请求信息,可以使用一个初始化对象作为第二个实参,例如:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    新增书签:<br>
    网址:<input id="url" type="text">
    <span id="message" style="color:red"></span><br>
    名称:<input type="text">

<script type="text/javascript">    
    function params(paraObj) {
        return Object.keys(paraObj)
                     .map(name => {
                         let paraName = encodeURIComponent(name);
                         let paraValue = encodeURIComponent(paraObj[name]);                         
                         return `${paraName}=${paraValue}`.replace(/%20/g, '+');
                     })
                     .join('&');
    }

    document.getElementById('url').onblur = function() {
        let reqString = params({ 
            url : document.getElementById('url').value 
        });

        fetch('POST-1.php', {
            method : 'POST',
            headers : {
                'Content-Type' : 'application/x-www-form-urlencoded'
            },
            body : reqString
        })
        .then(resp => resp.text())
        .then(function(text) {
            if(text === 'existed') {
                document.getElementById('message').innerHTML = 'URL 已存在';
            }           
        });
    };

</script>

</body>
</html>

按我观看执行结果

body特性也支持FormData,例如将〈结合 FormData 上传文件〉中的范例,改使用 Fetch:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <form id="f" action="upload" method="post" enctype="multipart/form-data">
          Photo  :<input type="file" name="photo"/><br>
        <input id="upload" type="submit"/>
    </form> 

    <span id="message"></span>

<script type="text/javascript">

    document.getElementById('upload').onclick = function(evt) {
        let formData = new FormData(document.getElementById('f'));

        fetch('upload', {
            method : 'POST',
            body : formData
        })
        .then(function(resp) {
            if(resp.status === 200) {
                document.getElementById('message').innerHTML = 'File Uploaded';
            }  
        });

        evt.preventDefault();
    };

</script>

  </body>
</html>

Fetch 也支持跨站请求,可透过初始化对象的mode特性来设定'cors'(默认值)、'no-cors'(会发出跨域请求,然而无法读取响应)、'same-origin'(不发出跨域请求) 等值,例如〈跨站请求 CORS〉中的范例,可以如下改用 Fetch:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <body>
        ID:<input id="id">
        <button id="test">JSONP 测试</button>
        <span id="result"></span>
    </body>

<script type="text/javascript">

    document.getElementById('test').onclick = function() {
        let id = document.getElementById('id').value;
        let result = document.getElementById('result');

        fetch(`https://openhome.cc/Gossip/ECMAScript/samples/CORS-1.php?id=${id}`, {
            mode : 'cors' // 'cors' 是默认值
        })
        .then(resp => resp.json())
        .then(person =>  result.innerHTML = `${person.name}, ${person.age}`);
    };

</script>    

</body>
</html>

按我观看执行结果

其他初始化可以设定的特性,可以参考〈WindowOrWorkerGlobalScope.fetch〉文件的说明。

可以看到的是,对于简单的请求,使用 Fetch 的方便性,远大于XMLHttpRequest


展开阅读全文