前端请求优化之路:从传统到现代

186 阅读2分钟

在前端开发中,与后端进行数据交互是一项非常重要的任务。今天我们将通过分析 index.htmlajax.html 两个文件中的代码,来深入了解前端请求的优化过程,以及如何将传统的 XMLHttpRequest(XHR)对象转换为可以使用 awaitPromise 对象。

index.html 看代码优化过程

初始状态与问题

index.html 中,我们的目标是从 GitHub API 获取用户的仓库信息,并将其展示在页面上。一开始,我们面临几个问题:

  1. 页面加载时机:使用 window.onload 事件会导致执行时间较晚,因为它要等到页面所有资源(包括图片等)都加载完成才会触发。
  2. 代码繁琐:使用 then 方法进行链式调用时,代码会变得冗长且难以维护。

第一步:使用 DOMContentLoaded 事件

document.addEventListener('DOMContentLoaded', async() => {
    // 代码逻辑
})

DOMContentLoaded 事件会在 HTML 文档解析完成后立即触发,而不需要等待所有资源加载完成。这样可以提前执行我们的代码,提高页面响应速度。

第二步:从 then 链式调用到 async/await

最初,我们使用 then 方法进行链式调用:

fetch('https://api.github.com/users/yourgithub').then(res => res.json()).then(data => {
    document.getElementById('repos').innerHTML = data.map(item => {
        `<li>${item.name}</li>`.join('')
    })
})

这种方式代码比较繁琐,而且嵌套层次过多时会影响代码的可读性。我们可以使用 async/await 来优化:

const result = await fetch ('https://api.github.com/users/yourgithub/repos')
const data = await result.json()
console.log(data);
document.getElementById('repos').innerHTML = 
data.map(item => `<li>${item.name}</li>`).join('');

async/await 让异步代码看起来像同步代码,提高了代码的可读性和可维护性。await 关键字会暂停当前函数的执行,直到 Promise 对象状态变为 fulfilledrejected,然后返回结果。

完整优化后的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JS 请求</title>
    <script>
        document.addEventListener('DOMContentLoaded', async() => {
            const result = await fetch ('https://api.github.com/users/yourgithub/repos')
            const data = await result.json()
            console.log(data);
            document.getElementById('repos').innerHTML = 
            data.map(item => `<li>${item.name}</li>`).join('');
        })
    </script>
</head>
<body>
    <ul id="repos">

    </ul>
</body>
</html>

ajax.html 中将 XHR 转换为可 awaitPromise 对象

传统 XHR 的问题

在早期,我们使用 XMLHttpRequest 对象进行接口请求。这种方式需要手动处理状态变化和错误,代码比较复杂,而且没有 Promise 的支持,难以使用现代的异步编程方式。

创建 Promise 封装 XHR

const getJSON = async function(url){
    return new Promise((resolve,reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET',url)
        xhr.send();
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4){
                resolve(JSON.parse(xhr.responseText))
            }
        }
    })
};

我们创建了一个 getJSON 函数,它返回一个 Promise 对象。在 Promise 的执行器函数中,我们实例化了一个 XMLHttpRequest 对象,打开请求并发送。然后通过 onreadystatechange 事件监听请求状态的变化,当 readyState 变为 4 时,表示响应内容已经到达,我们将响应文本解析为 JSON 并通过 resolve 方法返回。

使用 async/await 调用封装后的函数

(async () => {
    const data = await getJSON('https://api.github.com/users/yourgithub/repos')
    document.getElementById('repos').innerHTML = 
        data.map(item => `<li>${item.name}</li>`).join('');
})()

我们使用立即执行的异步函数表达式(IIFE)来调用 getJSON 函数,并使用 await 关键字等待 Promise 对象的结果。这样,我们就可以像处理同步代码一样处理异步请求。

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ajax</title>
</head>
<body>
    <ul id="repos"></ul>
<script>
const getJSON = async function(url){
    return new Promise((resolve,reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET',url)
        xhr.send();
        xhr.onreadystatechange = function() {
            if(xhr.readyState == 4){
                resolve(JSON.parse(xhr.responseText))
            }
        }
    })
};

(async () => {
    const data = await getJSON('https://api.github.com/users/yourgithub/repos')
    document.getElementById('repos').innerHTML = 
        data.map(item => `<li>${item.name}</li>`).join('');
})()
</script>
</body>
</html>

ps: 这里立即执行函数前面记得用;。自动分号插入(ASI)的局限性: ◦ 当上一行代码以([/+-开头时,ASI不会自动插入分号。可能导致解析器将IIFE与上一行代码合并解析。

总结

通过对 index.htmlajax.html 代码的分析,我们看到了前端请求代码的优化过程。从使用传统的 XMLHttpRequest 到使用现代的 fetch API,再到使用 async/await 语法糖,我们的代码变得更加简洁、易读和易于维护。同时,将 XHR 封装为 Promise 对象,让我们可以更好地使用现代异步编程方式。在实际开发中,我们应该根据项目需求和兼容性要求选择合适的请求方式。

完整代码:GitHub