## 2. 跨域、axios配置與api管理
在這個項目中,我們使用axios進行數據請求
> axios中文文檔: http://www.hmoore.net/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))
```
瀏覽器顯示以下信息,說明引入成功
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))
```
瀏覽器會因為同源策略報錯

下面進行跨域的配置
> 配置目錄 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))
```
然后就發現瀏覽器訪問成功了

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. 可以做到接口一次定義,到處使用