## 2. 跨域、axios配置与api管理
在这个项目中,我们使用axios进行数据请求
> axios中文文档: https://www.kancloud.cn/yunye/axios/234845
```shell
# 安装axios
npm/cnpm i axios -S # -S 指安装到package.json中的dependencies中
```
安装完成后,我们要在main.js中引入,然后测试一下是否成功引入
```js
//main.js文件
import axios from 'axios'
axios.get('https://api.github.com/users?since=10') //使用github接口做一下测试
.then(res=>console.log(res))
.catch(err=>console.log(err))
```
浏览器显示以下信息,说明引入成功![image-20190301160510216](https://ws4.sinaimg.cn/large/006tKfTcgy1g0nculisayj30p2047mxz.jpg)
github提供的接口配置了cors,所以我们能够能够在浏览器正常访问到,但cors兼容性最低到ie10,而且后台不一定会配置cors,所以在开发时我们需要配置一下跨域
参考链接:
1. cors详解 http://www.ruanyifeng.com/blog/2016/04/cors.html
### 2.1配置跨域
> 参考文档:https://segmentfault.com/a/1190000017905030
>
先找个没有设置cors的api使用axios访问一下
```js
axios.get('http://118.24.85.97:22222/api')
.then(res=>console.log(res))
.catch(err=>console.log(err))
```
浏览器会因为同源策略报错
![image-20190307094529285](https://ws3.sinaimg.cn/large/006tKfTcgy1g0tzlds507j31e401kwez.jpg)
下面进行跨域的配置
> 配置目录 config/index.js 13行
```js
proxyTable: {
'/apis':{
target:'http://118.24.85.97:22222',//后台地址 proxyTable 把/apis映射成target 即 /apis=http://118.24.85.97:22222
changeOrigin:true,//是否跨域
pathRewrite:{
'^/apis':''
}
}
}
```
再进行访问数据时就要在接口前面加上/apis(/apis就相当于http://118.24.85.97:22222)
```js
axios.get('/apis/api')
.then(res=>console.log(res))
.catch(err=>console.log(err))
```
然后就发现浏览器访问成功了
![image-20190307095002857](https://ws2.sinaimg.cn/large/006tKfTcgy1g0tzq4mvtnj30qy00zq31.jpg)
proxyTable原理:跨域是浏览器禁止的,服务端并不禁止跨域 ,所以浏览器可以发给自己的服务端然后,由自己的服务端再转发给要跨域的服务端,做一层代理。proxyTable使用的是```http-proxy-middleware```中间件,内部用的是http-proxy
以上配置的跨域是开发环境下的,在生产环境就自动失效了,而且这样配置我们开发时访问接口时,都要写成```/apis/xxx/xxx```格式,在部署到服务器中时,我们要把/apis拿掉,才能访问到正确的url。有两种方法,一种是在开发环境中设置(通过axios的baseURL),另一种是在服务器上修改nginx的配置设置。
### 2.2生产环境去除/apis前缀
在这里详细说下第一种方式,原理是这样的:
通过检测是开发环境和生产环境,设置不同的baseURL,使生产环境和开发环境都能正确访问url
在src目录下新建一个```apis```目录,然后在apis目录下新建一个```api.config.js```文件
```js
//判断是否是生产环境
//webpack在开发环境和生产环境分别执行不同的js文件,process.env.NODE_ENV设置了不同的值,process.env.NODE_ENV在生产环境中值为'production'(这个值是在build/build.js中第4行设置的)
var isPro = process.env.NODE_ENV=== 'production'
// 如果是生产环境 我们就使用服务器的uri,如果是开发环境,我们就添加/apis前缀
module.exports = {
baseUrl: isPro ? 'http://118.24.85.97:22222' : '/apis'
}
```
在main.js中引入这个文件,然后设置axios的```baseURL```
```js
//引入api.config.js文件,然后设置axios的baseURL
import apiConfig from './apis/api.config'
axios.defaults.baseURL=apiConfig.baseUrl
```
再来测试一下不加/apis的接口
```js
axios.get('/api')
.then(res=>console.log(res))
.catch(err=>console.log(err))
```
浏览器显示是ok的。这样我们以后使用axios访问接口就可以不加/apis了,打包后访问也不用手动去除/apis
### 2.3 api统一管理
> 在vue项目开发过程中,会涉及到很多接口的处理,当项目足够大时,就需要统一管理接口。
>
> 具体方法应该挺多的,这里只介绍一种:使用axios+async/await进行接口的统一管理
一般来说,后台的接口是分模块的,例如我们后台的测试接口
* 身份认证 /api/login /api/reg
* 用户信息 /v1/api/user
我们首先在src目录下新建一个apis文件夹,后台提供的所有接口都在这里定义
第二步,按照后台提供的模块新建js文件,我们新建```user.js``` ```auth.js```
第三步,引入axios,做相应的配置
在apis目录下新建一个http.js,在里面做axios相应的配置
1. 我们上文中是在main.js文件引入的axios,设置的baseURL,以上代码可以去除,改为在http.js中引入
2. 我们做的主要是:引入axios,创建一个axios的实例(实例的功能和axios一样)
```js
import axios from 'axios'
import apiConfig from './api.config'
//创建axios的一个实例
var instance = axios.create({
baseURL:apiConfig.baseUrl,
timeout: 6000
})
//------------------- 一、请求拦截器 后面介绍
instance.interceptors.request.use(function (config) {
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
//----------------- 二、响应拦截器 后面介绍
instance.interceptors.response.use(function (response) {
return response.data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
/**
* 使用es6的export default导出了一个函数,导出的函数代替axios去帮我们请求数据,
* 函数的参数及返回值如下:
* @param {String} method 请求的方法:get、post、delete、put
* @param {String} url 请求的url:
* @param {Object} data 请求的参数
* @returns {Promise} 返回一个promise对象,其实就相当于axios请求数据的返回值
*/
export default function (method, url, data = null) {
method = method.toLowerCase();
if (method == 'post') {
return instance.post(url, data)
} else if (method == 'get') {
return instance.get(url, { params: data })
} else if (method == 'delete') {
return instance.delete(url, { params: data })
}else if(method == 'put'){
return instance.put(url,data)
}else{
console.error('未知的method'+method)
return false
}
}
```
第四步,在```apis/xxx.js```文件中引入http.js导出的函数,拿其中一个文件```auth.js```说明
```js
//auth.js 用于定义用户的登录、注册、注销等
import req from './http.js'
//定义接口
//在这里定义了一个登陆的接口,把登陆的接口暴露出去给组件使用
export const LOGIN =params=>req('post','/api/users/login',params)
//这里使用了箭头函数,转换一下写法:
// export const LOGIN=function(params){
// return req('post','/api/login',params)
// }
//定义注册接口
export const REG =params=>req('post','/api/users/reg',params)
```
最后一步,在需要用的该api的组件中引入并调用,我们在App.vue文件中测试下
```js
<template>
<div>
<h2>登录</h2>
用户名<input type="text" v-model="user">
密码<input type="password" v-model="pass">
<input type="button" @click="reg" value="注册">
<input type="button" @click="login" value="登录">
</div>
</template>
<script>
import {LOGIN,REG} from '../../apis/auth.js'
export default {
data() {
return {
user:'',
pass:'',
err:[]
}
},
methods: {
async reg(){
try {
const data = await REG({ name: this.user,pass: this.pass })
console.log(data)
alert(JSON.stringify(data))
this.cleanForm()
} catch (error) {
console.log(error)
}
},
async login(){
try {
const data = await LOGIN({ name: this.user,pass: this.pass })
alert(JSON.stringify(data))
this.cleanForm()
} catch (error) {
console.log(error)
}
},
cleanForm(){
this.user=''
this.pass=''
}
},
}
</script>
```
注:如果要打开Login.vue,需要配置对应的路由
上面的代码引入了`auth.js`定义的api,并在对应的方法中使用。代码中用到了async/await,其实很简单,可以假设async是个标识,说明这个函数中有异步请求,await翻译为'等',后面接一个异步请求,等后面的异步请求执行完成之后,会把结果赋给`=`左边的值
> 参考链接 http://www.runoob.com/w3cnote/es6-async.html
总结一下,像上面那样定义接口虽然麻烦点,但有两个好处:
1. 代码看起来规范,所有的接口都在一个文件夹定义,不用分散的各个组件,维护起来简单,例如后台的一些url变了,改起来也方便
2. 可以做到接口一次定义,到处使用