目录

🛒 Vue 中的 AJAX

# 解决开发环境 AJAX 跨域问题

引入问题:

  • 使用 node 配置两个模拟的服务器:
点击查看
  • server1.js
const express = require('express')
const app = express()

app.use((request,response,next)=>{
   console.log('有人请求服务器1了');
   // console.log('请求来自于',request.get('Host'));
   // console.log('请求的地址',request.url);
   next()
})

app.get('/students',(request,response)=>{
   const students = [
      {id:'001',name:'tom',age:18},
      {id:'002',name:'jerry',age:19},
      {id:'003',name:'tony',age:120},
   ]
   response.send(students)
})

app.listen(5050,(err)=>{
   if(!err) console.log('服务器1启动成功了,请求学生信息地址为:http://localhost:5050/students');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • server2.js
const express = require('express')
const app = express()

app.use((request,response,next)=>{
   console.log('有人请求服务器2了');
   next()
})

app.get('/cars',(request,response)=>{
   const cars = [
      {id:'001',name:'奔驰',price:199},
      {id:'002',name:'马自达',price:109},
      {id:'003',name:'捷达',price:120},
   ]
   response.send(cars)
})

app.listen(5051,(err)=>{
   if(!err) console.log('服务器2启动成功了,请求汽车信息地址为:http://localhost:5051/cars');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 在 Vue 组件使用 axios 请求服务器:

首先引入 axios

import axios from 'axios'
1

然后编写想服务器请求信息的方法:

getStudentInfo(){
  axios.get('http://localhost:5050/students').then(
      response => {
        console.log('请求成功了', response.data)
      },
      error => {
        console.log('请求失败', error)
      }
  )
}
1
2
3
4
5
6
7
8
9
10

运行后,此时出现跨域问题:

  • 没有遵循同源策略(协议名、主机名、端口号要一致)(由于 Vue 组件基于 http://localhost:8080 运行,而服务器基于 http://localhost:5050

  • 虽然引起了跨域但是请求有成功发送。发送请求后,服务器也收到了请求,并且返回了响应,但是浏览器发现了跨域问题导致报错。

image-20220404164541243

一般有三种解决的方法:

  • cors :服务器响应请求时携带特殊的相应头。(真正意义上解决跨域,但是响应头不能轻易携带)
  • jsonp :巧妙借助 scriptsrc 属性在引入外部资源时不受同源策略实现。但是在真正开发中,得在前端使用特殊方法,后端同时配合;并且只能对 get 请求有效。
  • 配置代理服务器:本身也是服务器,与前台所处的位置(意思是端口)一致,要发送请求时,代理服务器作为中介帮助前台发送请求到服务器 ,服务器返回的请求也通过代理服务器作为中介返回给前台。利用服务器与服务器之间的通信不使用 ajax 而使用 http 请求这一点。
    • 后端 nginx 开启代理服务器。
    • 借助 vue-cli 开启代理服务器。

# 借助 Vue-CLI 配置代理服务器

🔗 devServer-proxyConfiguration Reference | Vue CLI (vuejs.org) (opens new window)

第一种方法:

  • vue.js.config 配置文件中配置 devServer
// 开启代理服务器
devServer: {
    proxy: 'http://localhost:5050'
}
1
2
3
4
  • 修改原来请求的路径的端口为代理服务器的端口,并且注意请求的路径要明确:
getStudentInfo(){
  axios.get('http://localhost:8080/students').then(
      response => {
        console.log('请求成功了', response.data)
      },
      error => {
        console.log('请求失败', error)
      }
  )
}
1
2
3
4
5
6
7
8
9
10

运行后,请求成功并获得了响应的数据。

提示

使用上述方法配置代理服务器有两个缺陷

  • 优先匹配前端资源:代理服务器并不是将所有的请求都会转发到服务器,当代理服务器中已经存在的资源(在 public 目录下,只要请求路径同名),则不会向服务器请求资源。
  • 通过在 vue.config.js 中的 devServer不可以配置多个代理服务器

第二种方法:

  • vue.config.js 配置文件中配置 devServer
devServer: {
    proxy: {
        '/server1': { // 请求前缀,请求地址例子:http://localhost:8080/server1/students
            target: 'http://localhost:5050', // 代理目标服务器的基础历经
            pathRewrite: {'^/server1': ''}, // 重写路径,匹配以 /server1开头的请求路径
            ws: true, // 用于支持websocket
            changeOrigin: true // 用于控制请求头中的host值 例子 false为8080,true为5050
        },
        '/server2': { // 请求前缀,请求地址例子:http://localhost:8080/server2/cars
            target: 'http://localhost:5051', 
            pathRewrite: {'^/server2': ''}, 
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

提示

解决了再方法一中的缺点:

  • 可以配置多个服务器
  • 请求时加上前缀,解决了当代理服务器可能有重复命名的文件导致请求不能转发到服务器的问题。

但是使用该方法配置略烦琐,并且请求资源时必须要添加前缀。

# 配置代理案例(搜索案例)

  • 按照组件化编程,划分好组件,实现页面效果:

image-20220406124911501

  • 在搜索组件中完成请求搜索:

🔗 GitHub 提供的用户搜索接口:

https://api.github.com/search/users?q=userName
1

在搜索输入框中使用 v-model 绑定输入的关键词,搜索按钮绑定搜索事件方法:

<div>
  <input type="text" placeholder="enter the name you search" v-model="keyword"/>
  <button @click="searchUsers">Search</button>
</div>
1
2
3
4

使用 axios 发送请求:

data() {
  return {
    keyword: ''
  }
}, methods: {
  searchUsers() {
    axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
        response => {
          console.log('请求成功', response.data)
        },
        error => {
          console.log('请求出错', error.massage)
        }
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 要在请求链接中插入表达式,要使用 `` 符号。
  • 不用考虑跨域问题,因为 Github 后端已经解决了 CORS 引起的跨域问题(添加特殊的响应头)。 同时 incomplete_results 意思是仅展示部分结果, 搜索结果保存在 items 中。
  • 注意不能过于频繁请求接口,否则会被误认为攻击然后被封锁 IP。

image-20220406130342246

  • 通过全局事件总线在 Search 组件和 List 组件之间的通信。

List 组件中绑定自定义事件:

mounted() {
  this.$bus.$on('getUserResults', (users) => {
    console.log('List received data,', users)
    this.users = users
  })
}
1
2
3
4
5
6

Search 组件中触发,并传入数据:

searchUsers() {
  axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
      response => {
        console.log('请求成功', response.data.items)
        this.$bus.$emit('getUserResults', response.data.items)
      },
      error => {
        console.log('请求出错', error.massage)
      }
  )
}
1
2
3
4
5
6
7
8
9
10
11

将获取到的数据应用到模版,使用 v-for 循环:

<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: 100px'/>
    </a>
    <p class="card-text">{{ user.login }}</p>
  </div>
1
2
3
4
5
6
7

# Vue-resource

  • 安装 vue-resource
$ npm i vue-resource
1
  • 在入口文件 main.js 引入:
// 引入 vue-resource
import VueResource from 'vue-resource'

// 使用插件
Vue.use(VueResource)
1
2
3
4
5

🌰 使用的方法与 axios 类似:

searchUsers() {
  this.$bus.$emit('updateListData', {isFirst: false, isLoading: true, errMsg: '', users: []})
  this.$http.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
      response => {
        console.log('请求成功', response.data.items)
        this.$bus.$emit('updateListData', {isLoading: false, errMsg: '', users: response.data.items})
      },
      error => {
        console.log('请求出错', error.message)
        this.$bus.$emit('updateListData', {isLoading: false, errMsg: error.message, users: []})

      }
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

提示

一般在 Vue 的早期版本较多使用来实现异步加载,Vue 2.0 版本后,一般推荐使用 axios 实现 AJAX 请求。

🔗 Vue.js Ajax (vue-resource) | 菜鸟教程 (runoob.com) (opens new window)

📢 上次更新: 2022/09/02, 10:18:16