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>
本段代码来自 https://www.codeqd.com/post/20250620917.html
这里要注意两个细节:
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(...)
本段代码来自 https://www.codeqd.com/post/20250620917.html
用 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)
}
}
}
本段代码来自 https://www.codeqd.com/post/20250620917.html
场景 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)
})
}
}
本段代码来自 https://www.codeqd.com/post/20250620917.html
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 => {
// 这里也能捕获错误
})
}
}
本段代码来自 https://www.codeqd.com/post/20250620917.html
这样封装后,所有请求都会自动加 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('加载用户信息失败~')
}
}
}
本段代码来自 https://www.codeqd.com/post/20250620917.html
这种写法的好处是:代码层级更少,逻辑更直观,尤其是多个请求有依赖关系时(比如先拿用户 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)
}
})
本段代码来自 https://www.codeqd.com/post/20250620917.html
场景 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(...)
本段代码来自 https://www.codeqd.com/post/20250620917.html
场景 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')
本段代码来自 https://www.codeqd.com/post/20250620917.html
场景 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(...)
本段代码来自 https://www.codeqd.com/post/20250620917.html
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
}
本段代码来自 https://www.codeqd.com/post/20250620917.html
第二步:在组件里触发 action
<template>
<div>
<p>用户名:{{ userInfo.username }}</p>
<p>邮箱:{{ userInfo.email }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
// 从 Vuex 的 user
本段代码来自 https://www.codeqd.com/post/20250620917.html
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。