1.fetch 是啥?在 Vue2 里主要用来做什么?
在 Vue2 项目里,很多同学会纠结“该用 axios 还是 fetch 发请求”,也常碰到 fetch 报错不知咋处理、想加拦截器没思路这些问题,这篇文章用问答形式,把 Vue2 结合 fetch 开发时的关键知识点、避坑技巧讲透,帮你搞懂 fetch 在 Vue2 里的用法逻辑~
fetch 是**浏览器原生的网络请求 API**,基于 Promise 设计,能让我们在前端发起 HTTP 请求(像 GET、POST 这些),从后端接口拿数据或者提交数据,在 Vue2 项目里,它的核心作用和 axios 类似——给组件、Vuex 等模块提供“和后端交互”的能力,比如页面初始化时拉取列表数据、表单提交时把用户输入发过去。和 axios 最大的区别是:axios 是第三方封装库(得 npm 安装),而 fetch 是浏览器自带的,不用额外装依赖,fetch 本身是“底层 API”,不像 axios 那样把很多常用功能封装好(比如自动处理响应 JSON、拦截器、取消请求这些),所以用的时候得自己写不少逻辑。
Vue2 里用 fetch 发 GET 请求,代码咋写?
最基础的场景是“组件创建时拉取列表数据”,步骤分 3 步:发起请求 → 解析响应 → 把数据存到组件 data 里,直接看代码示例(假设要请求商品列表接口):
<template> <div> <ul> <li v-for="item in goodsList" :key="item.id">{{ item.name }}</li> </ul> </div> </template> <script> export default { data() { return { goodsList: [] // 用来存接口返回的商品列表 } }, created() { // 组件创建后就发请求 // 1. 发起 GET 请求(默认就是 GET,options 可以省略) fetch('https://api.example.com/goods') // 2. 解析响应:response.json() 会把响应体转成 JSON 对象(注意这步是异步的) .then(response => response.json()) // 3. 把数据赋值给 data .then(data => { this.goodsList = data }) // 4. 捕获错误(比如网络断了、接口 404 这些情况) .catch(error => { console.error('获取商品列表失败:', error) // 也可以给用户提示,this.$message.error('加载失败~') }) } } </script>
这里要注意两个细节:
response.json()
是异步操作(因为响应体可能很大,浏览器要慢慢读),所以必须用.then
处理,如果接口返回的不是合法 JSON 格式,这一步会报错,要在catch
里处理。- fetch 对“状态码非 2xx”的情况(404、500)不会直接 reject,得自己判断
response.ok
,比如接口返回 404 时,response.ok
是false
,但 fetch 不会进catch
,得手动抛错:
fetch(...) .then(response => { if (!response.ok) { // 状态码不是 2xx,主动抛错,让错误进到 catch 里 throw new Error(`请求失败,状态码:${response.status}`) } return response.json() }) .then(...) .catch(...)
用 fetch 发 POST 请求,带参数咋处理?
POST 请求分两种常见场景:传 JSON 格式数据(比如提交表单)、传 FormData 格式数据(比如上传文件),写法不一样,一个个说。
场景 1:传 JSON 格式数据
要给请求头加 Content-Type: application/json
,并且把参数用 JSON.stringify
转成字符串,示例:提交用户注册信息
methods: { async submitRegister() { const userData = { username: this.username, password: this.password, email: this.email } try { const response = await fetch('https://api.example.com/register', { method: 'POST', // 指定请求方法是 POST headers: { 'Content-Type': 'application/json' // 告诉后端传的是 JSON }, body: JSON.stringify(userData) // 把对象转成 JSON 字符串 }) if (!response.ok) { throw new Error('注册失败,请检查参数~') } const resData = await response.json() console.log('注册成功,返回数据:', resData) // 比如跳转到登录页:this.$router.push('/login') } catch (error) { console.error('注册出错:', error) // 给用户提示:this.$message.error(error.message) } } }
场景 2:传 FormData 格式数据(比如上传文件)
这种情况不用手动设置 Content-Type
,浏览器会自动加 multipart/form-data
,示例:上传用户头像
methods: { handleFileChange(e) { const file = e.target.files[0] if (!file) return const formData = new FormData() formData.append('avatar', file) // 第一个参数是后端要的字段名,第二个是文件 formData.append('username', this.username) // 还能传其他参数,比如用户名 fetch('https://api.example.com/upload', { method: 'POST', body: formData // 直接把 FormData 实例丢进去 }) .then(response => { if (!response.ok) throw new Error('上传失败') return response.json() }) .then(data => { console.log('上传成功,头像地址:', data.avatarUrl) this.avatar = data.avatarUrl // 把返回的头像地址赋值给页面 }) .catch(error => { console.error('上传错误:', error) }) } }
fetch 和 axios 在 Vue2 项目里该怎么选?
很多同学纠结“用哪个更方便”,得看项目需求和场景,先对比核心差异:
维度 | fetch(原生 API) | axios(第三方库) |
---|---|---|
是否需安装 | 不需要(浏览器自带) | 需要 npm install axios 安装 |
错误处理 | 4xx/5xx 不会自动 reject,需手动判 response.ok |
状态码非 2xx 会自动 reject |
响应处理 | 需手动 response.json() 转 JSON |
自动把响应转成 JSON(除非配置了其他格式) |
拦截器 | 没有内置拦截器,需自己封装 | 内置请求拦截器、响应拦截器 |
取消请求 | 需用 AbortController 手动实现 |
内置 cancelToken 方便取消 |
浏览器兼容 | IE 完全不支持,移动端需看版本(如 Android 4.4+) | 兼容 IE(需配 polyfill),移动端友好 |
选择建议:
- 小项目、快速原型开发:用 fetch,不用额外装依赖,代码轻量。
- 中大型项目、需要拦截器(比如统一加 token、统一处理错误)、需要兼容旧浏览器、需要方便取消请求:选 axios,能省很多自己封装的工作量。
想给 fetch 加“请求拦截”“响应拦截”,咋实现?
axios 内置了拦截器,fetch 没有,得自己封装一个“请求工具函数”来模拟拦截逻辑,核心思路是:在发请求前统一处理参数(比如加 token),在响应返回后统一处理错误(token 过期跳登录)。
示例:封装一个 fetchWrapper
函数,同时实现请求拦截和响应拦截
// 假设项目里有 router(Vue Router),用来跳转页面 import router from '@/router' // 封装后的 fetch 工具函数 function fetchWrapper(url, options = {}) { // —— 请求拦截逻辑 —— // 1. 加公共请求头(token) const token = localStorage.getItem('token') // 从 localStorage 拿 token const defaultHeaders = { 'Authorization': `Bearer ${token}`, // 假设后端用 Bearer token 鉴权 'Content-Type': 'application/json' // 也可以加其他公共头 } // 合并用户传的 headers 和公共 headers(用户传的优先级更高) options.headers = { ...defaultHeaders, ...options.headers } // 2. 其他请求前操作:比如加载中动画(如果有全局 loading 组件) // this.$loading.show() —— 注意:这里不能直接用 this,得想其他方式管理 loading,Vuex // —— 发起请求 —— return fetch(url, options) .then(response => { // —— 响应拦截逻辑 —— // 1. 处理通用错误(token 过期,状态码 401) if (response.status === 401) { // 跳转到登录页,并且清除无效 token localStorage.removeItem('token') router.push('/login') throw new Error('登录状态失效,请重新登录~') } // 2. 处理其他状态码错误(500 服务器错误) if (!response.ok) { throw new Error(`请求失败,状态码:${response.status}`) } return response }) .then(response => { // 把响应转成 JSON(如果接口返回 JSON 的话) return response.json() }) .catch(error => { // 全局错误捕获:比如弹 Toast 提示用户 console.error('请求出错:', error) // this.$message.error(error.message) —— 同样,这里要考虑怎么全局调用 throw error // 把错误抛出去,让调用方也能 catch }) } // —— 在组件里使用封装后的 fetchWrapper —— export default { created() { fetchWrapper('https://api.example.com/user') .then(data => { console.log('用户信息:', data) }) .catch(error => { // 这里也能捕获错误 }) } }
这样封装后,所有请求都会自动加 token、自动处理 401 跳转、自动解析 JSON,和 axios 的拦截器逻辑就很像了~
Vue2 里用 fetch 处理异步,咋和 async/await 结合?
fetch 本身返回 Promise,所以可以用 async/await
让代码更像“同步写法”,结构更清晰,核心是用 try...catch
捕获错误,代替 .then().catch()
链式调用。
示例:在组件 created 钩子拉取用户信息
export default { data() { return { userInfo: {} } }, async created() { // 注意这里要加 async try { // 发起请求(await 等待请求完成) const response = await fetch('https://api.example.com/user') // 判断响应是否成功 if (!response.ok) { throw new Error('获取用户信息失败') } // 解析响应体(await 等待解析完成) const data = await response.json() // 赋值给 data this.userInfo = data } catch (error) { // 捕获所有错误:网络问题、状态码错误、解析错误等 console.error('获取用户信息出错:', error) // 给用户提示:this.$message.error('加载用户信息失败~') } } }
这种写法的好处是:代码层级更少,逻辑更直观,尤其是多个请求有依赖关系时(比如先拿用户 ID,再拿用户详情),用 async/await
嵌套更清晰。
fetch 常见错误咋处理?有哪些典型场景?
fetch 踩坑最多的就是“错误不自动捕获”“跨域”“解析失败”这些问题,得针对性处理:
场景 1:网络问题(比如断网)
当用户设备没网时,fetch 会直接 reject,错误会进到 catch
里,可以在 catch
里提示用户检查网络:
.catch(error => { if (error.message.includes('Failed to fetch')) { // 这种情况一般是网络问题 console.error('网络好像断了~请检查网络设置') } else { console.error('其他错误:', error) } })
场景 2:响应状态码非 2xx(404、500)
前面提过,fetch 不会自动把这些状态码当错误,得手动判 response.ok
,可以在解析响应前抛错:
fetch(...) .then(response => { if (!response.ok) { // 把状态码和响应内容一起抛出去,方便调试 return response.text().then(text => { throw new Error(`状态码${response.status},响应内容:${text}`) }) } return response.json() }) .catch(...)
场景 3:跨域问题(CORS)
如果后端接口没配置 CORS(跨域资源共享),浏览器会直接拦截响应,fetch 会报错,这种情况前端改不了,得让后端同学在响应头加:
Access-Control-Allow-Origin: * // 或者指定前端域名 Access-Control-Allow-Methods: GET, POST, PUT, DELETE // 允许的请求方法 Access-Control-Allow-Headers: Content-Type, Authorization // 允许的请求头
开发阶段可以用 Vue2 的代理解决跨域(配置 vue.config.js
):
// vue.config.js module.exports = { devServer: { proxy: { '/api': { // 把以 /api 开头的请求代理到目标域名 target: 'https://api.example.com', changeOrigin: true, // 开启跨域 pathRewrite: { '^/api': '' } // 去掉请求路径里的 /api } } } } // 组件里请求时,把 url 改成 /api/xxx fetch('/api/goods')
场景 4:响应解析失败(比如接口返回非 JSON 格式)
如果接口返回的不是 JSON(比如返回 HTML、纯文本),用 response.json()
会报错,可以先用 response.text()
看内容,再处理:
fetch(...) .then(response => { if (!response.ok) { ... } // 先转成文本,判断是否是 JSON return response.text().then(text => { try { return JSON.parse(text) // 尝试转 JSON } catch (parseError) { return text // 转不了,返回原文本 } }) }) .then(data => { console.log('响应数据:', data) }) .catch(...)
Vue2 结合 Vuex 时,fetch 咋管理异步数据?
Vuex 是 Vue2 里的状态管理工具,用 fetch 从接口拿数据后,要把数据存到 Vuex 的 state 里,步骤是:在 action 里发请求 → 提交 mutation 改 state。
示例:用 fetch 拉取用户信息并存到 Vuex
第一步:写 Vuex 模块(user.js)
// src/store/modules/user.js const state = { userInfo: {} // 存用户信息 } const mutations = { // 定义修改 userInfo 的 mutation SET_USER_INFO(state, payload) { state.userInfo = payload } } const actions = { // 定义异步 action,用 fetch 拉取数据 async fetchUserInfo({ commit }) { try { const response = await fetch('https://api.example.com/user') if (!response.ok) { throw new Error('获取用户信息失败') } const data = await response.json() // 提交 mutation,把数据存到 state commit('SET_USER_INFO', data) } catch (error) { console.error('fetchUserInfo 出错:', error) // 可以 dispatch 其他 action 处理错误,比如记录日志 } } } export default { namespaced: true, // 开启命名空间,避免模块间冲突 state, mutations, actions }
第二步:在组件里触发 action
<template> <div> <p>用户名:{{ userInfo.username }}</p> <p>邮箱:{{ userInfo.email }}</p> </div> </template> <script> import { mapState } from 'vuex' export default { computed: { // 从 Vuex 的 user
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。