跳转至

关于前端

1 MDN

什么是MDN?这是一个web工程师的开发者社区

2 React

React是一个构建JS的工具,类似于前端的框架。他能让开发着用积木一样的感觉构建网页

  • 能让网页开发变得更加简单
  • 最流行的前端开发工具之一

3 Code with AI

AI其实是构建前端很好的工具,他比人写的要美观多了。——zzh

4 HTML

我们使用MDN进行学习。

https://developer.mozilla.org/zh-CN/docs/Learn_web_development/Core/Structuring_content

5 前端基本运转

因此,与传统模型不同,许多网站使用 JavaScript API 从服务器请求数据,并在不重新加载页面的情况下更新页面。因此,当用户搜索新产品时,浏览器仅请求更新页面所需的数据——例如要显示的新书集。

这里主要的 APIFetch API。它允许页面中运行的 JavaScript 向服务器发起 HTTP 请求来获取特定的资源。当服务器提供了这些资源时,JavaScript 可以使用这些数据更新页面(通常是通过使用 DOM 操作 API)。请求的数据通常是 JSON,这是一种很好的传输结构化的格式,但也可以是 HTML 或纯文本。

6 JSON

JavaScript 对象表示法(JSON)是用于将结构化数据表示为 JavaScript 对象的标准格式,通常用于在网站上表示和传输数据(例如从服务器向客户端发送一些数据,因此可以将其显示在网页上)。你会经常遇到它,所以在本文中,我们向你提供使用 JavaScript 处理 JSON 的所有工作,包括访问 JSON 对象中的数据项并编写自己的 JSON

6.1 什么是JSON

JSON 是一种按照 JavaScript 对象语法的数据格式,这是道格拉斯·克罗克福特推广的。虽然它是基于 JavaScript 语法,但它独立于 JavaScript,这也是为什么许多程序环境能够读取(解读)和生成 JSON

JSON 可以作为一个对象或者字符串存在,前者用于解读 JSON 中的数据,后者用于通过网络传输 JSON 数据。这不是一个大事件——JavaScript 提供一个全局的 可访问的 JSON 对象来对这两种数据进行转换。

7 fetch api

7.1 前情提要

咱们用一个生活中的例子来理解:

生活中的"承诺" (Promise)

想象一下,你去一家很火的奶茶店点单:

  1. 你点单 (发起 fetch 请求):你告诉店员你要一杯珍珠奶茶。
  2. 店员给你小票 (返回 Promise 对象):店员不会立刻给你奶茶,因为制作需要时间。他会给你一张小票,上面写着你的单号。这张小票就是一个“承诺”(Promise)——承诺你最终会拿到东西(奶茶或者一个坏消息,比如没珍珠了)。
    • 这个 Promise 此刻的状态是 "pending" (进行中),你还不知道结果。
  3. 等待叫号 (Promise 的状态变化)
    • 情况A:奶茶做好了 (Promise resolved/fulfilled - 成功):店员叫到你的号,你凭小票拿到了奶茶。
    • 情况B:没珍珠了 (Promise rejected - 失败):店员叫到你的号,告诉你珍珠卖完了,问你要不要换别的。
  4. 拿到奶茶后你做什么 (第一个 .then)
    • 如果奶茶做好了(Promise 成功了),你拿到奶茶。这个“拿到奶茶”的动作,就相当于 Promise 成功后执行的第一个函数。
    • fetch 成功后,你拿到的不是直接的数据,而是一个 Response 对象(响应对象),可以理解为“装着奶茶的杯子,但你还没尝味道”。你需要对这个 Response 对象做一些处理,比如把它转换成我们能看懂的 JSON 格式数据。
  5. 尝味道/处理数据 (第二个 .then)
    • 你把奶茶的盖子打开,尝了一口(比如 response.json()response.text())。这个动作也需要一点点时间,所以它本身也可能返回一个新的 Promise。
    • 当数据真正被解析完毕(比如 JSON 数据准备好了),你就可以对这个数据做你想做的事情了(比如打印出来,或者在页面上显示)。
  6. 处理意外情况 (.catch)
    • 如果在任何一步出错了(比如店员告诉你没珍珠了,或者奶茶打翻了),你就需要处理这个“坏消息”。.catch() 就是专门用来捕获和处理这些错误的。
  7. 无论如何都要做的事 (.finally):
    • 不管你最终是拿到了奶茶,还是被告知没珍珠了,你最终都会离开奶茶店。.finally() 里的代码就是无论 Promise 成功还是失败,都会执行的代码,通常用来做一些清理工作。

现在我们看 fetch.then 的语法

fetch 的基本语法是: fetch(url, [options])

  • url: 你要请求的网址。
  • options (可选): 一个配置对象,可以设置请求方法 (GET, POST 等)、请求头 (headers)、请求体 (body) 等。

核心:fetch() 返回一个 Promise 对象。

Promise 对象有几个关键方法:

  • .then(onFulfilled, onRejected):

    • 当 Promise 成功 (fulfilled) 时,会调用 onFulfilled 这个函数,并且把成功的结果作为参数传给它。
    • 当 Promise 失败 (rejected) 时,会调用 onRejected 这个函数(可选),并且把失败的原因作为参数传给它。
    • 重要.then() 本身也会返回一个新的 Promise!这就是为什么我们可以链式调用 .then().then()...
  • .catch(onRejected):

    • 这是一个更常用的捕获错误的方式,相当于 .then(null, onRejected) 的语法糖。它会捕获前面任何一个 Promise 链条中发生的错误。
  • .finally(onFinally):

    • 无论 Promise 最终是成功还是失败,onFinally 这个函数都会被执行。它不接收任何参数。

一步步分解 fetch 的典型用法:

fetch('https://api.example.com/data') // 1. 发起请求,返回一个 Promise (承诺1: 会给你一个响应)
  .then(response => { // 2. 当承诺1成功时 (服务器有响应了)
    // response 是一个 Response 对象,还不是我们直接能用的数据
    // 需要检查响应是否成功 (比如 HTTP 状态码 200-299)
    if (!response.ok) { // response.ok 是个布尔值,true 表示成功
      throw new Error('网络响应出错了: ' + response.statusText); // 如果不成功,抛出错误,会被下面的 .catch 捕获
    }
    // 如果响应是 JSON 格式,我们调用 response.json() 来解析它
    // response.json() 本身也返回一个新的 Promise (承诺2: 会把响应体解析成 JSON)
    return response.json();
  })
  .then(data => { // 3. 当承诺2成功时 (JSON 数据解析完毕)
    // data 就是我们最终想要的 JavaScript 对象或数组
    console.log('成功获取到数据:', data);
    // 在这里处理你的数据,比如更新页面
  })
  .catch(error => { // 4. 如果上面任何一个 Promise 失败了 (比如网络错误、解析错误)
    console.error('请求失败了:', error);
    // 在这里处理错误,比如给用户提示
  })
  .finally(() => { // 5. 无论成功还是失败,最后都会执行
    console.log('请求处理完成,无论成功与否。');
    // 比如隐藏加载动画
  });

解释每一步:

  1. fetch('https://api.example.com/data'):

    • 向服务器发送一个 GET 请求。
    • 它立刻返回一个 Promise 对象 (我们叫它 promise1)。此时 promise1 的状态是 "pending"。
  2. .then(response => { ... }):

    • 这个 .then 是注册在 promise1 上的。
    • 如果网络请求成功(服务器返回了响应,哪怕是 404 或 500 错误,也算网络层面成功),promise1 变为 "fulfilled" 状态,并且它的值是一个 Response 对象。
    • 箭头函数 response => { ... } 就会被执行,response 就是那个 Response 对象。
    • 在函数内部:
      • if (!response.ok): 我们检查 HTTP 状态码。如果不是 2xx (成功状态),response.ok 会是 false。我们主动 throw new Error(...) 来让这个 Promise 链条进入 "rejected" 状态,这样错误就能被 .catch 捕获。
      • return response.json();: 如果 response.oktrue,我们调用 response.json()。这个方法会读取响应体并尝试将其解析为 JSON重要的是,response.json() 也返回一个 Promise (我们叫它 promise2)。因为读取和解析响应体也需要时间。这个 promise2 会成为当前 .then 返回的 Promise。
  3. .then(data => { ... }):

    • 这个 .then 是注册在 promise2 上的(也就是上一个 .thenresponse.json() 返回的那个 Promise)。
    • response.json() 成功解析出 JSON 数据后,promise2 变为 "fulfilled" 状态,并且它的值就是解析后的 JavaScript 对象或数组。
    • 箭头函数 data => { ... } 就会被执行,data 就是那个解析好的数据。
    • 这里你就可以使用 data 了。
  4. .catch(error => { ... }):

    • 如果在 fetch 本身(比如网络断开)、或者第一个 .then(比如 response.okfalse 时我们 throw Error)、或者第二个 .then(比如 response.json() 解析失败,虽然不太常见,但如果响应体不是有效的 JSON,它会 reject),任何一个环节出错了,这个 .catch 就会被触发。
    • error 对象包含了错误信息。
  5. .finally(() => { ... }):

    • 无论整个过程是成功(一路 .then 下来)还是失败(被 .catch 捕获),finally 里的回调函数总会被执行。

总结一下为什么 .then 会让人困惑:

  1. 异步性: 代码不是从上到下立刻执行完的。.then 里的函数要等前面的 Promise 完成了才会执行。
  2. 回调函数: .then(fn) 里的 fn 是一个回调函数,它不是立刻执行的,而是被 Promise 系统在未来某个时刻调用的。
  3. 链式调用和 Promise 的传递: 每个 .then 都会返回一个新的 Promise。如果 .then 的回调函数返回一个普通值,那么新的 Promise 会立刻 resolve 并携带这个值。如果回调函数返回另一个 Promise,那么新的 Promise 的状态会依赖于这个内部返回的 Promise。response.json() 就是返回 Promise 的典型例子。

7.2 基本讲解

1. 什么是 Fetch API?

Fetch API 是一种现代的、基于 Promise 的 JavaScript 接口,用于发起网络请求(HTTP请求)。它是 XMLHttpRequest (XHR) 的一个更强大、更灵活的替代品。

2. 核心特点

  • 基于 Promise: 使得异步代码处理更简洁,避免了回调地狱。
  • 更简洁的 API: 相比 XHR,语法更直观。
  • Request 和 Response 对象: 提供了强大的 Request 和 Response 对象,可以灵活处理请求和响应的各个方面(如头部、主体、状态码等)。
  • 跨平台: 可在浏览器和 Node.js (v18+ 内置,早期版本需 node-fetch 模块) 中使用。

3. 基本用法 (GET 请求)

fetch('https://api.example.com/data') // 发起 GET 请求
  .then(response => {
    // response 是一个 Response 对象
    // 检查 HTTP 状态码是否表示成功 (200-299)
    if (!response.ok) {
      throw new Error('网络响应错误: ' + response.statusText); // 如果不ok,抛出错误
    }
    return response.json(); // 将响应体解析为 JSON,.json() 返回一个新的 Promise
    // 其他解析方法: response.text(), response.blob(), response.formData()
  })
  .then(data => {
    // data 是解析后的 JSON 数据
    console.log(data);
  })
  .catch(error => {
    // 处理网络错误或 .then() 中抛出的错误
    console.error('请求失败:', error);
  });

关键点: * fetch() 返回一个 Promise。 * 这个 Promise 会在接收到服务器的**响应头**后立即 resolve,得到一个 Response 对象。 * Response 对象本身并不包含实际数据,你需要调用像 .json().text() 这样的方法来提取响应体,这些方法也会返回 Promise。 * 重要: fetch() 只有在发生网络错误(如无法连接服务器)时才会 reject。对于 HTTP 错误状态码(如 404, 500),fetch() 仍然会 resolve,你需要通过检查 response.ok (true 表示状态码 200-299) 或 response.status 来判断请求是否真的成功。

4. 发送不同类型的请求 (如 POST)

const postData = {
  username: 'john_doe',
  email: 'john.doe@example.com'
};

fetch('https://api.example.com/users', {
  method: 'POST', // 请求方法
  headers: {
    'Content-Type': 'application/json', // 告诉服务器我们发送的是 JSON
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN' // 示例:认证头部
  },
  body: JSON.stringify(postData) // 请求体,需要转换为 JSON 字符串
})
.then(response => {
  if (!response.ok) {
    // 可以尝试读取错误信息体
    return response.json().then(errData => {
        throw new Error(`HTTP error! status: ${response.status}, message: ${errData.message || 'Unknown error'}`);
    }).catch(() => { // 如果错误信息体不是json或者解析失败
        throw new Error(`HTTP error! status: ${response.status}, statusText: ${response.statusText}`);
    });
  }
  // 如果是201 Created,可能没有响应体,或响应体是新创建的资源
  if (response.status === 204 || response.headers.get("content-length") === "0") {
      return null; // No content
  }
  return response.json();
})
.then(data => {
  if (data) {
    console.log('创建成功:', data);
  } else {
    console.log('操作成功,无返回内容。');
  }
})
.catch(error => {
  console.error('POST 请求失败:', error);
});

fetch() 的第二个参数 (options 对象)常用属性: * method: 请求方法 (e.g., 'GET', 'POST', 'PUT', 'DELETE'). 默认为 'GET'. * headers: 一个包含请求头的对象或 Headers 对象实例。 * body: 请求体。可以是 String, Blob, BufferSource, FormData, URLSearchParams, ReadableStream。对于 JSON 数据,通常使用 JSON.stringify()。 * mode: 请求模式 (e.g., 'cors', 'no-cors', 'same-origin'). * credentials: 是否发送 cookies (e.g., 'include', 'same-origin', 'omit'). * cache: 缓存模式 (e.g., 'default', 'no-store', 'reload'). * signal: 一个 AbortSignal 对象,用于中止请求。

5. 处理响应 (Response 对象)

Response 对象包含服务器返回的所有信息: * response.ok: 布尔值,true 表示 HTTP 状态码在 200-299 之间。 * response.status: HTTP 状态码 (e.g., 200, 404, 500)。 * response.statusText: HTTP 状态文本 (e.g., "OK", "Not Found")。 * response.headers: 一个 Headers 对象,可以用来获取响应头信息 (e.g., response.headers.get('Content-Type'))。 * response.url: 响应的 URL。 * 解析响应体的方法 (都返回 Promise): * response.json(): 解析为 JSON 对象。 * response.text(): 解析为纯文本。 * response.blob(): 解析为 Blob 对象 (用于处理文件/二进制数据)。 * response.formData(): 解析为 FormData 对象。 * response.arrayBuffer(): 解析为 ArrayBuffer (用于处理原始二进制数据)。

6. 错误处理

  • 网络错误: fetch() 返回的 Promise 会 reject (e.g., DNS 查询失败,服务器无响应)。这些错误会直接进入 .catch() 块。
  • HTTP 错误: 如 404 (Not Found) 或 500 (Internal Server Error)。fetch() 不会 reject 这些情况,而是会 resolve 一个 Response 对象。你需要**手动检查 response.okresponse.status** 并在必要时 throw new Error(),这样才能在 .catch() 中统一处理。
fetch('https://api.example.com/non-existent-endpoint')
  .then(response => {
    if (!response.ok) {
      // 如果 HTTP 状态码表明有错误,我们构造一个错误并抛出
      // 这样可以被后续的 .catch() 捕获
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => {
    // 这个 catch 会捕获网络错误和上面手动抛出的 HTTP 错误
    console.error('捕获到错误:', error.message);
  });

7. Headers 对象

可以用来创建和管理 HTTP 头部。

const myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
myHeaders.append('X-Custom-Header', 'MyValue');

fetch('https://api.example.com/data', { headers: myHeaders });

// 或者直接使用对象字面量
fetch('https://api.example.com/data', {
  headers: {
    'Content-Type': 'application/json',
    'X-Custom-Header': 'MyValue'
  }
});

8. Request 对象 (可选)

可以先创建一个 Request 对象,然后再传递给 fetch()。这在需要复用请求配置或在 Service Workers 中拦截请求时很有用。

1
2
3
4
5
6
7
8
9
const myRequest = new Request('https://api.example.com/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: 'Hello' })
});

fetch(myRequest)
  .then(response => response.json())
  .then(data => console.log(data));

9. 优点总结

  • 现代、标准。
  • Promise 支持,代码更优雅。
  • 功能强大,可定制性高。
  • 与 Service Workers 等现代 Web API 良好集成。

10. 注意事项

  • CORS (跨域资源共享): 默认情况下,Fetch API 受同源策略限制。进行跨域请求时,服务器需要正确配置 CORS 头部。
  • Cookies: 默认情况下,fetch() **不会**发送或接收跨域 cookies。如果需要,设置 credentials: 'include'
  • 无超时设置: Fetch API 本身没有内置的请求超时机制。需要通过 AbortController 结合 setTimeout 来实现。
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 5000); // 5秒超时
    
    fetch('/slow-resource', { signal: controller.signal })
      .then(response => { /* ... */ })
      .catch(error => {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted due to timeout');
        } else {
          console.error('Fetch error:', error);
        }
      })
      .finally(() => {
        clearTimeout(timeoutId); // 清除超时
      });
    
  • 错误处理细节: 再次强调,HTTP 错误状态码 (4xx, 5xx) 不会使 fetch() Promise reject