js发ajax请求的几种方式
- xhr(原生js自带): new XMLHttpRequest() xhr.open() xhr.send()
- jQuery: $.get() $.post
- axios(推荐)
- fetch(windows对象自带): 有致命缺陷...
-
安装 axios
num i axios
-
demo演示
- 后端接口的地址: http://127.0.0.1:8888/demo/ - 返回正常的数据是这样: { "data": [ { "id": 1, "name": "JimGreen", "age": 20 }, { "id": 2, "name": "KateGreen", "age": 18 }, { "id": 3, "name": "LiLei", "age": 20 }, { "id": 4, "name": "HanMeiMei", "age": 19 } ] }
### Student.vue(一个按钮发送请求,控制台打印返回信息)
<template>
<button type="button" @click="getStudentName">获取学生信息</button>
</template>
<script>
import axios from 'axios'
export default {
name:'Student',
methods:{
getStudentName(){
axios.get('http://127.0.0.1:8888/demo/').then(
response => {console.log('请求成功',response.data)},
error => {console.log('请求失败',error.message)}
)
}
}
}
</script>
<style>
</style>
- 结果: 由于浏览器的同源策略,跨域失败(服务器有收到请求,但是返回的数据被浏览器阻止了)
- 前端运行的地址: http://localhost:8080/
- 后端运行的地址: http://localhost:8000/demo/
- 区别: 协议&&域名 都一样,但是端口不一样,所以不同源,被拒绝
...been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
跨域的解决方案
- cors # 真正解决跨域问题(后端处理)
- jsonp: 利用 <script src>,只能发送get请求(前后端一起配合...)
- 代理服务器
-
本次讲的就是使用'代理服务器'解决问题
-
代理服务器
- nginx - vue-cli: 架了一个和客户端 端口一样的服务器
-
vue-cli 代理服务器解决跨域问题
### vue.config.js(配置完,一定要重启脚手架服务器,否则不生效) const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ ...... // 代理服务器 devServer: { // 配置目标服务器地址 proxy: 'http://localhost:8000' } }) ### Student.vue <template> <button type="button" @click="getStudentName">获取学生信息</button> </template> <script> import axios from 'axios' export default { name:'Student', methods:{ getStudentName(){ // 注意: 不再是向目标服务器发起请求 // 而是向本机的代理服务器发起请求+目标服务器的路径 axios.get('http://localhost:8080/demo/').then( response => {console.log('请求成功',response.data)}, error => {console.log('请求失败',error.message)} ) } } } </script> - 返回结果: 正常返回数据了 ata: Array(4) 0: {id: 1, name: 'JimGreen', age: 20} 1: {id: 2, name: 'KateGreen', age: 18} 2: {id: 3, name: 'LiLei', age: 20} 3: {id: 4, name: 'HanMeiMei', age: 19} length: 4 - 注意事项: 若脚手架的代理服务器本身就有 demo路径的资源,那么请求会优先得到代理服务器的响应 代理服务器不再进行转发 - 这种配置方式的缺陷有两点 - 不能配置多个代理 - 代理服务器有的资源,就不再进行转发
-
配置多个代理服务器演示
- 后端接口的地址1: http://127.0.0.1:8888/demo/test1 - 后端接口的地址2: http://127.0.0.1:8888/demo/test2 - 地址1返回数据 { "data": [ { "id": 1, "name": "JimGreen", "age": 20 }, { "id": 2, "name": "KateGreen", "age": 18 }, { "id": 3, "name": "LiLei", "age": 20 }, { "id": 4, "name": "HanMeiMei", "age": 19 } ] } - 地址2返回数据 { "data": [ { "id": 1, "name": "安宁", "age": 20 }, { "id": 2, "name": "云帆", "age": 18 }, { "id": 3, "name": "东东", "age": 20 }, { "id": 4, "name": "林林", "age": 19 } ] }
### vue.config.js
......
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
......
// 代理服务器
// devServer: {
// // 配置目标服务器地址
// proxy: 'http://localhost:8888'
// }
devServer: {
proxy: {
'/test1': { // 加前缀
target: 'http://localhost:8888', // 目标服务器地址
pathRewrite:{'^/test1':''} // 正则匹配,去掉这个前缀
// ws: true, // 用于支持 websocket
// changeOrigin: true // 用于控制请求头中的host值(是否隐藏客户端地址)
},
'/test2': {
target: 'http://localhost:8888',
pathRewrite:{'^/test2':''}
......
},
}
}
})
### Student.vue
<template>
<div>
<button type="button" @click="getStudentName">获取学生信息</button>
<button type="button" @click="getChineseName">获取中文信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:'Student',
methods:{
getStudentName(){
// axios.get('http://localhost:8080/demo/').then(
// 代理服务器地址+目标服务器资源路径
axios.get('http://localhost:8080/demo/test1/').then(
response => {console.log('请求成功',response.data)},
error => {console.log('请求失败',error.message)}
)
},
getChineseName(){
// axios.get('http://localhost:8080/demo/').then(
axios.get('http://localhost:8080/demo/test2/').then(
response => {console.log('请求成功',response.data)},
error => {console.log('请求失败',error.message)}
)
},
}
}
</script>
<style>
</style>
vue脚手架配置代理
-
方法一
### vue.config.js ...... devServer: { // 配置目标服务器地址 proxy: 'http://localhost:8888' }
- 优点: 配置简单,请求资源时直接发给前端代理服务器即可 - 缺点: 不能配置多个代理,不能灵活的控制请求是否走代理
-
方法二
### vue.config.js ...... devServer: { proxy: { '/test1': { // 取个名字 target: 'http://localhost:8888', // 目标服务器地址 pathRewrite:{'^/test1':''} // 正则匹配,去掉这个名字 // ws: true, // 用于支持 websocket // changeOrigin: true // 用于控制请求头中的host值(是否隐藏客户端地址) }, '/test2': { target: 'http://localhost:8888', pathRewrite:{'^/test2':''} ...... }, } }
- 优点: 可以配置多个代理,且可以灵活控制请求是否走代理 - 缺点: 配置稍繁琐,请求资源必须加前缀
github搜索案例(调用github API渲染数据)
-
一个页面,就是一个'App.vue'
-
引入bootstrap.css(public新建css目录,把bootstrap.css丢里面)
### index.html <!DOCTYPE html> <html lang=""> <head> ...... <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <!--模仿上面的引入方式,变量和css之间,无需'/'--> <link rel="stylesheet" type="text/css" href="<%= BASE_URL %>css/bootstrap.css"/> ...... </head>
-
demo组件搭建(App+2个组件)
### Search.vue <template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="enter the name you search"/> <button>Search</button> </div> </section> </template> <script> export default { name: "Search" } </script> ### List.vue <template> <div class="row"> <div class="card"> <a href="" target="_blank"> <img src="https://img1.baidu.com/it/u=1629479163,3719818209&fm=253&fmt=auto&app=138&f=JPEG?w=473&h=399" style="width: 100%"/> </a> </div> </div> </template> <script> export default { name: "List" } </script> <style scoped> .album { min-height: 50rem; /* Can be removed; just added for demo purposes */ padding-top: 3rem; padding-bottom: 3rem; background-color: #f7f7f7; } .card { float: left; width: 33.333%; padding: .75rem; margin-bottom: 2rem; border: 1px solid #efefef; text-align: center; } .card > img { margin-bottom: .75rem; border-radius: 100px; } .card-text { font-size: 85%; } </style> ### App.vue <template> <div class="container"> <Search/> <List/> </div> </template> <script> import Search from './components/GitHub/Search.vue' import List from './components/GitHub/List.vue' export default { name: 'App', components: { Search, List } } </script>
-
引入第三方css注意点: 如果采用以下方式引用,一旦库依赖的第三方资源没有被引入,就会报错
<script>
import './assert/css/bootstrap.css'
......
</script>
-
github接口地址
https://api.github.com/search/users?q=xxx
-
测试该接口能否正常返回数据(正常返回数据)
### List.vue <template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <!--v-model收集用户输入--> <input type="text" placeholder="enter the name you search" v-model="keyWord" /> <button @click="searchUser">Search</button> <!--绑定事件--> </div> </section> </template> <script> import axios from 'axios' export default { name: "Search", data(){ return { keyWord:'' } }, methods:{ searchUser(){ // 返回的数据: {total_count: 117470, incomplete_results: false, items: Array(30)} axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { console.log('请求成功',response.data) }, error => { console.log('请求失败',error.message) } ) } } } </script>
-
现在调用github api接口,获取数据并渲染出来(这里使用$bus实现组件之间通讯)
### Search.vue(发数据,触发事件并传参) <template> ...... </template> <script> import axios from 'axios' export default { name: "Search", ...... methods:{ searchUser(){ axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { // 触发事件并传参 this.$bus.$emit('getUsers',response.data.items) }, error => { console.log('请求失败',error.message) } ) } } } </script> ### List.vue(收数据,绑定事件并接收数据) <template> <div class="row"> <!--遍历每一项,渲染数据--> <div class="card" v-for="user in users" :key="user.login"> <a :href="user.html_url" target="_blank"> <img :src="user.avatar_url" style="width: 100%"/> </a> <p class="card-text">{{user.login}}</p> </div> </div> </template> <script> export default { name: "List", data(){ return { users:[] // 预存储 } }, mounted(){ this.$bus.$on('getUsers',(items)=>{ this.users = items // 接收数据并保存起来 }) } } </script> <style scoped> ...... </style>
-
List组件的四种状态切换,我们新增布尔值变量,来标识这种状态,再依据值来决定是否展示
- 刚进入页面,展示欢迎信息: Welcome - 用户点击搜索(网速比较慢): loading - 搜索成功: users - 搜索失败: error
-
基础写法(可以实现 功能,缺点是函数参数很业余,没有实现'语义化')
### List.vue <template> <div class="row"> ...... <!--增加html显示内容(依据标识值决定是否展示)--> <h1 v-show="isFirst">欢迎使用</h1> <h1 v-show="isLoading">加载中......</h1> <h1 v-show="errMsg">{{errMsg}}</h1> </div> </template> <script> export default { name: "List", data(){ return { isFirst:true, // 新增3个标记 isLoading:false, errMsg:'', users:[] } }, mounted(){ // this.$bus.$on('getUsers',(items)=>{ // this.users = items // }) // 接收 Search组件传过来的值 this.$bus.$on('updateListData',(isFirst,isLoading,errMsg,users)=>{ this.isFirst = isFirst this.isLoading = isLoading this.errMsg = errMsg this.users = users }) } } </script> ### Search.vue ...... searchUser(){ console.log('xxx') axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { console.log('请求成功',response.data) // this.$bus.$emit('getUsers',response.data.items) // 传参 this.$bus.$emit('updateListData',false,false,'',response.data.items) }, error=>{ console.log('请求失败',error.message) // 失败就返回错误信息和空list this.$bus.$emit('updateListData',false,true,error.message,[]) } ) }
-
改进版: 把那些零碎的函数参数,打包成一个对象,传入
### Search.vue searchUser(){ console.log('xxx') axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { // this.$bus.$emit('updateListData',false,false,'',response.data.items) // 传入一个对象(由于isFirst只显示一次,从此以后就是false,故这里不传,在List组件写死即可) this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items}) }, error=>{ console.log('请求失败',error.message) // this.$bus.$emit('updateListData',false,true,'网络错误',[]) // 同样的写法,传入一个对象 this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}) } ) } ### List.vue <template> <div class="row"> <!--根据length值是否展示用户信息--> <div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login"> <a :href="user.html_url" target="_blank"> <img :src="user.avatar_url" style="width: 100%"/> </a> <p class="card-text">{{user.login}}</p> </div> <h1 v-show="info.isFirst">欢迎使用</h1> <h1 v-show="info.isLoading">加载中......</h1> <h1 v-show="info.errMsg">{{info.errMsg}}</h1> </div> </template> <script> export default { name: "List", data(){ return { info:{ // 用对象来打包这些数据 isFirst:true, isLoading:false, errMsg:'', users:[] } } }, mounted(){ // this.$bus.$on('updateListData',(isFirst,isLoading,errMsg,users)=>{ // this.isFirst = isFirst // this.isLoading = isLoading // this.errMsg = errMsg // this.users = users // }) // 接收对象 this.$bus.$on('updateListData',(dataObj)=>{ // this._data = dataObj // 这样写,_data就被写死了,不再是响应式对象 this.info = {...this.info,...dataObj} // 拼接 this.info.isFirst = false; // 不再显示欢迎消息 }) } } </script> <style scoped> ...... </style>
vue-resource(旧版本vue使用的ajax库)
-
属于vue插件
-
使用方法很简单,和axios一模一样(API都一模一样)
### 安装 npm i vue-resource ### main.js(引入插件) import xxx from 'vue-resource' // 这里可以随便取名 Vue.use(xxx) // 使用插件 ### Search.vue earchUser(){ // 一模一样的API用法... // axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { ...... }, error=>{ ...... } ) }