目录
- 1.XMLHttpRequest发送请求
- 2.fetch发送请求
- 3.axios请求库(Vue中推荐写法)
- 模拟发送get和post请求
- 网络请求时发送用户认证信息
- 请求拦截器
- 响应拦截器
- 用户管理
在进行 Vue 的网络请求之前,我们先写一些假数据:
users.json:
[ | |
{ "id": 1, "name": "张三" }, | |
{ "id": 2, "name": "李四" } | |
] |
1.XMLHttpRequest发送请求
使用 XMLHttpRequest 请求 users.json 中的数据:
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<script> | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
mounted() { | |
const xhr = new XMLHttpRequest() | |
// // true 表示发送异步请求 | |
xhr.open('get', '/mock/users.json', true) | |
// // 写法1 | |
// xhr.onreadystatechange = () => { | |
// // 0 - 4 xhr.readyState状态值 | |
// if (xhr.readyState === 4 && xhr.status === 200) { | |
// console.log(JSON.parse(xhr.responseText)); | |
// this.users = JSON.parse(xhr.responseText) | |
// } | |
// } | |
// 写法2 | |
xhr.onload = ()=>{ | |
console.log(JSON.parse(xhr.responseText)); | |
this.users = JSON.parse(xhr.responseText) | |
} | |
xhr.send(null) | |
} | |
}) | |
</script> |
注意:上面介绍了两种方法,分别是onreadystatechange
和onload
,我们需要知道这两种方法的区别 。XMLHttpRequest 对象有一个属性 readyState,将其 (xhr.readyState) 打印后发现:进入onreadystatechange
请求方式中时,可以打印其状态为2,状态为3,状态为4;进入onload
之后,只出现了状态码4。也就是说,只有处于状态码4,请求已完成,响应已就绪的情况下,才会进入onload
。只要进入onload
请求中,一定是已经到4这个状态了。nreadystatechange()的定义是只要返回的状态码只要变化时就回调一次函数,而onload只有状态码为4时才能回调一次函数。
2.fetch发送请求
使用 fetch 请求 users.json 中的数据:
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<script> | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
// 兼容写法 | |
mounted() { | |
fetch('/mock/users.json') | |
.then(ret => ret.json()) | |
.then(json => this.users = json) | |
} | |
// 高版本写法 | |
// async mounted() { | |
// let ret = await fetch('/mock/users.json') | |
// this.users = await ret.json() | |
// } | |
// 高版本装逼写法 | |
// async mounted() { | |
// this.users = await (await fetch('/mock/users.json')).json | |
// } | |
}) | |
</script> |
3.axios请求库(Vue中推荐写法)
概述:
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和node.js中。能拦截请求和响应,自动转换JSON数据。axios也是vue作者推荐使用的网络请求库。
优势:
- 使用人群多
- 对 Ts 支持非常好
- 跨平台(nodejs、移动端)
- 基于 promise ,异步变同步,写法简单
- 添加了拦截器等封装好的函数,使用更加方便
使用方法:
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<script src="./js/axios.js"></script> | |
<script> | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
mounted() { | |
// 获取数据的简单使用 | |
axios.get('/mock/users.json').then(ret => { | |
console.log(ret.data); | |
this.users=ret.data; | |
}) | |
} | |
}) | |
</script> |
模拟发送get和post请求
创建自己的服务器,模拟发送 get 和 post 请求
步骤1:
使用npm init -y
初始化 node.js 配置文件
步骤2:
使用npm i -S express
,安装 express
步骤3:
新建app.js
,并且修改package.json
文件如下
准备工作完成后,我们就可以书写服务端代码了:
app.js:
const express = require('express') | |
const app = express() | |
app.listen(9000, () => { | |
console.log('http://localhost:9000') | |
}) | |
// 中间件 | |
// 解决跨域 npm i -S cors 下载并写入配置文件 | |
app.use(require('cors')()) | |
// post提供,内容为json格式 | |
app.use(express.json()) | |
app.get('/api/users', (req, res) => { | |
// 解决跨域问题 | |
// res.setHeader('Access-Control-Allow-Origin', '*') | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
data: { | |
users: [ | |
{ id: 1, name: '张三' }, | |
{ id: 2, name: '李四' } | |
], | |
// 获取用户认证信息 | |
token: req.headers?.token | |
} | |
}) | |
}) | |
app.post('/api/users', (req, res) => { | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
// 把传过来的参数再返回出去 | |
data: req.body | |
}) | |
}) | |
// patch/put | |
// patch 增量更新 | |
// put 全量更新 | |
app.put('/api/users/:id', (req, res) => { | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
data: req.body | |
}) | |
}) | |
app.delete('/api/users/:id', (req, res) => { | |
// 返回 204 意味着不会返回任何数据 | |
// res.status(204).send({ | |
res.status(200).send({ | |
code: 0, | |
msg: 'ok', | |
data: { | |
id: req.params.id | |
} | |
}) | |
}) |
前端获取数据:
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>vue学习使用</title> | |
<!-- 第1步: 引入vue库文件 --> | |
<script src="./js/vue.js"></script> | |
</head> | |
<body> | |
<!-- 第2步:挂载点 --> | |
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<!-- 第3步:实例化vue --> | |
<script src="./js/axios.js"></script> | |
<script> | |
// axios全局配置 这里的地址会自动补全到 get 或 post 要请求的地址中 | |
// 可以写在配置文件中,方便修改 | |
axios.defaults.baseURL = 'http://localhost:9000'; | |
// 全局设置网络请求超时时间,10s中还没请求到数据,就停止请求 | |
axios.defaults.timeout = 10000 | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
mounted() { | |
// 发送 get 请求 | |
// axios.get('/api/users').then(ret => { | |
// console.log(ret.data); | |
// // 只会监听当前 then 方法中是否有异常 | |
// }, () => console.log('有异常')) | |
// 发送 post 请求 | |
// axios.post('/api/users', { | |
// name: '李四' | |
// }).then(ret => { | |
// console.log(ret.data); | |
// }) | |
// 发送 put 请求 | |
// axios.put('/api/users', { | |
// name: '李四 -- put' | |
// }).then(ret => { | |
// console.log(ret.data); | |
// }) | |
// 发送 delete 请求 | |
axios.delete('/api/users/100').then(ret => { | |
console.log(ret.data); | |
}) | |
} | |
}) | |
</script> | |
</body> | |
</html> |
注意:
超时请求不到数据就报出异常,这种捕获异常的方式,会监听前面所有的 then 方法
axios.get('/api/users').then(ret => { | |
console.log(ret.data); | |
}).catch(() => console.log('有异常')) |
下面这种写法,只会监听当前 then 方法中是否有异常:
axios.get('/api/users').then(ret => { | |
console.log(ret.data); | |
},() => console.log('有异常')) |
网络请求时发送用户认证信息
有时候我们通过网络请求一个服务器的时候,需要携带一个用户认证信息,例如用户只有在登录状态下才能获取服务端的信息,否则不能获取数据,即所谓的接口认证。
那么这个登录的状态,即用户的认证信息,这是怎样传给服务器的呢?
我们会将用户的认证信息包含在前端的请求头当中,发送给服务端。
以 get 请求为例,服务端代码如下:
const express = require('express') | |
const app = express() | |
app.listen(9000, () => { | |
console.log('http://localhost:9000') | |
}) | |
// 中间件 | |
// 解决跨域 | |
app.use(require('cors')()) | |
// post提供,内容为json格式 | |
app.use(express.json()) | |
app.get('/api/users', (req, res) => { | |
// 解决跨域问题 | |
// res.setHeader('Access-Control-Allow-Origin', '*') | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
data: { | |
users: [ | |
{ id: 1, name: '张三' }, | |
{ id: 2, name: '李四' } | |
], | |
// 获取用户认证信息 | |
token: req.headers?.token | |
} | |
}) | |
}) |
前端代码如下:
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>vue学习使用</title> | |
<!-- 第1步: 引入vue库文件 --> | |
<script src="./js/vue.js"></script> | |
</head> | |
<body> | |
<!-- 第2步:挂载点 --> | |
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<!-- 第3步:实例化vue --> | |
<script src="./js/axios.js"></script> | |
<script> | |
// axios全局配置 这里的地址会自动补全到 get 或 post 要请求的地址中 | |
// 可以写在配置文件中,方便修改 | |
axios.defaults.baseURL = 'http://localhost:9000'; | |
// 全局设置网络请求超时时间,10s中还没请求到数据,就停止请求 | |
axios.defaults.timeout = 10000 | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
mounted() { | |
// 发送 get 请求 | |
axios.get('/api/users', { | |
// 认证信息放在请求头发送 | |
headers: { | |
token: 'abc' | |
} | |
}).then(ret => { | |
console.log(ret.data); | |
// 只会监听当前 then 方法中是否有异常 | |
}, () => console.log('有异常')) | |
} | |
}) | |
</script> | |
</body> | |
</html> |
注意:我们还可以使用全局配置来发送用户认证信息:axios.defaults.headers.token = 'aaaaa'
,(请求头中的 token 因该删掉,否则会覆盖全局配置)但是这样的配置会给所有的请求方法都添加上用户认证信息,不能区别对待,也就是不能进行业务处理,不够灵活,所以我们需要用到拦截器。
请求拦截器
延续上面的案例,如果我们的需求是,如果是 get 方法发送,则在请求头中添加用户认证信息,其他方法不添加,则前端页面需要这样写:
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>vue学习使用</title> | |
<!-- 第1步: 引入vue库文件 --> | |
<script src="./js/vue.js"></script> | |
</head> | |
<body> | |
<!-- 第2步:挂载点 --> | |
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<!-- 第3步:实例化vue --> | |
<script src="./js/axios.js"></script> | |
<script> | |
// axios全局配置 这里的地址会自动补全到 get 或 post 要请求的地址中 | |
// 可以写在配置文件中,方便修改 | |
axios.defaults.baseURL = 'http://localhost:9000'; | |
// 全局设置网络请求超时时间,10s中还没请求到数据,就停止请求 | |
axios.defaults.timeout = 10000 | |
// 请求拦截器 | |
axios.interceptors.request.use(config => { | |
if (config.method === 'get') { | |
config.headers.token = 'abc' | |
} | |
// 请求拦截器中的回调函数中的config对象一定要return出去 | |
return config | |
}) | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
mounted() { | |
// 发送 get 请求 | |
axios.get('/api/users', { | |
// 认证信息放在请求头发送 | |
headers: { | |
token: 'abc' | |
} | |
}).then(ret => { | |
console.log(ret.data); | |
// 只会监听当前 then 方法中是否有异常 | |
}, () => console.log('有异常')) | |
} | |
}) | |
</script> | |
</body> | |
</html> |
注意:请求拦截器中的回调函数中的config对象一定要return出去。
响应拦截器
作用:
对响应的数据做处理或判断
应用:
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>vue学习使用</title> | |
<!-- 第1步: 引入vue库文件 --> | |
<script src="./js/vue.js"></script> | |
</head> | |
<body> | |
<!-- 第2步:挂载点 --> | |
<div id="app"> | |
<ul> | |
<li v-for="item in users" :key="item.id">{{item.name}}</li> | |
</ul> | |
</div> | |
<!-- 第3步:实例化vue --> | |
<script src="./js/axios.js"></script> | |
<script> | |
// axios全局配置 这里的地址会自动补全到 get 或 post 要请求的地址中 | |
// 可以写在配置文件中,方便修改 | |
axios.defaults.baseURL = 'http://localhost:9000'; | |
// 全局设置网络请求超时时间,10s中还没请求到数据,就停止请求 | |
axios.defaults.timeout = 10000 | |
axios.defaults.headers.token = 'aaaaa' | |
// 响应拦截器 | |
axios.interceptors.response.use(res => { | |
// 这里是未被处理过的响应数据 | |
console.log(res); | |
// 解套,减少数据的调用层级,直接返回 res.data | |
if (res.data.code == 0) return res.data | |
// 这里是做了全局的网络错误处理 | |
alert('有错') | |
}, err => Promise.reject(err)) | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
users: [] | |
}, | |
mounted() { | |
// 发送 get 请求 | |
axios.get('/api/users').then(ret => { | |
// 这里返回的时解套后的数据(已经被响应拦截器处理过了) | |
console.log(ret); | |
// 只会监听当前 then 方法中是否有异常 | |
}, () => console.log('有异常')) | |
} | |
}) | |
</script> | |
</body> | |
</html> |
用户管理
服务端:
// 服务端代码:具备增删改查、数据源 | |
const express = require('express') | |
const app = express() | |
app.listen(9000, () => { | |
console.log('http://localhost:9000') | |
}) | |
// 解决跨域 | |
app.use(require('cors')()) | |
// post提供,内容为json格式 | |
app.use(express.json()) | |
// 声明一个变量,只要服务不重启,我就永久存储(放入内存了) | |
let users = [ | |
{ id: 1, name: '张三', age: 10 }, | |
{ id: 2, name: '李四', age: 20 } | |
] | |
// 响应get请求 | |
app.get('/api/users', (req, res) => { | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
// 如果请求成功就返回对应的users | |
data: users | |
}) | |
}) | |
// 增添数据 | |
app.post('/api/users', (req, res) => { | |
// 接受post提交过来的数据,用当前时间作为主键id | |
const user = { ...req.body, id: Date.now() } | |
// 添加数据 | |
users.push(user) | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
// 如果请求成功就返回对应的users | |
data: users | |
}) | |
}) | |
// 修改数据 | |
app.put('/api/users/:id', (req, res) => { | |
// 接受修改的id号 | |
let id = req.params.id | |
// 接受put提交过来的数据 | |
users = users.map(item => (item.id == id ? { ...item, ...req.body } : item)); | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
data: users | |
}) | |
}) | |
// 删除数据 | |
app.delete('/api/users/:id', (req, res) => { | |
// 接受修改的id号 | |
let id = req.params.id | |
users = users.filter(item => item.id != id) | |
res.send({ | |
code: 0, | |
msg: 'ok', | |
data: users | |
}) | |
}) |
客户端:
http.js(axios发送请求):
// 解套处理(响应拦截器),直接返回所需数据,降低层级 | |
axios.interceptors.response.use( | |
res => res.data, | |
err => Promise.reject(err) | |
) | |
// 请求拦截器 | |
axios.interceptors.request.use( | |
config => { | |
config.baseURL = 'http://localhost:9000' | |
// 请求超时拦截 | |
config.timeout = 10000 | |
return config | |
}, | |
err => Promise.reject(err) | |
) | |
// 封装 axios 中的方法 | |
const get = url => axios.get(url) | |
const post = (url, data = {}) => axios.post(url, data) | |
const put = (url, data = {}) => axios.put(url, data) | |
const del = url => axios.delete(url) |
index.html:
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>vue学习使用</title> | |
</head> | |
<body> | |
<div id="app"> | |
<button @click="add">添加用户</button> | |
<hr> | |
<ul> | |
<!-- 条件渲染 --> | |
<!-- template标签 影子标签,可以占位,可以读取指令,但不会被解析成html,可以使有效的标签中没有不想要的指令 --> | |
<template v-if="users.length===0"> | |
<li>数据加载中...</li> | |
</template> | |
<template v-else> | |
<li v-for="item in users" :key="item.id"> | |
<span>{{item.name}}</span> | |
<span> | |
<button @click="edit(item.id)">修改</button> | |
</span> | |
<span> | |
<button @click="del(item.id)">删除</button> | |
</span> | |
</li> | |
</template> | |
</ul> | |
</div> | |
<script src="./js/vue.js"></script> | |
<script src="./js/axios.js"></script> | |
<script src="./utils/http.js"></script> | |
<script> | |
const vm = new Vue({ | |
el: '#app', | |
data: { | |
// 定义数据源 | |
users: [] | |
}, | |
async mounted() { | |
// this.users = await (await get('/api/users')).data | |
let users = await get('/api/users') | |
this.users = users.data | |
}, | |
methods: { | |
async add() { | |
let users = await post(`/api/users`, { name: Date.now() + '' }) | |
this.users = users.data | |
}, | |
async edit(id) { | |
let users = await put(`/api/users/${id}`, { name: Date.now() + '' }) | |
this.users = users.data | |
}, | |
async del(id) { | |
let users = await del(`/api/users/${id}`) | |
this.users = users.data | |
} | |
} | |
}) | |
</script> | |
</body> | |
</html> |