本文將實如今 Vue
框架中使用 Typescript
+ Element-ui
以單例模式個性化封裝 Axios
, 知足咱們項目的所需,留個贊再走吧vue
typescript 是 JavaScript 的強類型版本。而後在編譯期去掉類型和特有語法,生成純粹的 JavaScript 代碼。因爲最終在瀏覽器中運行的仍然是 JavaScript,因此 TypeScript 並不依賴於瀏覽器的支持,也並不會帶來兼容性問題。node
TypeScript 是 JavaScript 的超集,這意味着他支持全部的 JavaScript 語法。並在此之上對 JavaScript 添加了一些擴展,如 class / interface / module 等。這樣會大大提高代碼的可閱讀性。ios
與此同時,TypeScript 也是 JavaScript ES6 的超集,Google 的 Angular 2.0 也宣佈採用 TypeScript 進行開發。這更是充分說明了這是一門面向將來而且腳踏實地的語言。sql
下面咱們列出了緣由,爲何咱們應該擁抱TypeScript:typescript
強類型語言的優點在於靜態類型檢查。歸納來講主要包括如下幾點:數據庫
Axios 是一個基於 promise 的 HTTP 庫,能夠用在瀏覽器和 node.js 中。npm
// 爲給定 ID 的 user 建立請求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 上面的請求也能夠這樣作
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
複製代碼
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
複製代碼
爲方便起見,爲全部支持的請求方法提供了別名編程
咱們大體瞭解了 Axios 以及一些經常使用的方法, 那接下來咱們就開始進入正題吧element-ui
$ vue create my-vue-typescript
複製代碼
上下鍵選擇,空格鍵肯定json
接下來是一些常規選項
下面是詢問要不要記錄此次配置以便後面直接使用,咱們選擇y
Utils
文件夾以及 咱們今天的主角 request.ts
文件Element-ui
$ npm i element-ui -S
複製代碼
qs
$ npm i qs -S
複製代碼
qs 是一個增長了一些安全性的查詢字符串解析和序列化字符串的庫
Axios
$ npm i axios -S
複製代碼
在此我不會爲你們講解太多 Ts 的知識,但在開始以前想讓你們明白 Typescript
中的幾個點,否則無法繼續下去
TypeScript裏的類型註解是一種輕量級的爲函數或變量添加約束的方式
# 咱們指定了 hello 這個變量必須是 string 類型
const hello: string = 'Hello World'
# 咱們指定了 greeter 傳入的 person 參數必須是 string 類型
function greeter(person: string) {
return "Hello, " + person;
}
複製代碼
在TypeScript裏,只在兩個類型內部的結構兼容那麼這兩個類型就是兼容的。 這就容許咱們在實現接口時候只要保證包含了接口要求的結構就能夠,而沒必要明確地使用 implements語句
interface IFamilyData {
father: string
mom: string
son: string
}
function getFamily(family: IFamilyData) {
return `爸爸${family.father},媽媽${family.mom},兒子${family.son}`
}
const family = { father: 'Jack', mom: 'Ruth', son: 'Bieber' }
document.body.innerHTML = getFamily(family)
複製代碼
TypeScript支持JavaScript的新特性,好比支持基於類的面向對象編程
class Person{
// 增長兩個屬性
name:string
age:number
// 增長能夠傳參的構造方法
constructor(name:string,age:number){
this.name = name
this.age = age
}
// 增長一個自定義的普通的打印函數
print(){
return this.name + ':'' + this.age
}
// 使用上面建立的類
// var p = new Person() // 這裏在使用上面的類時沒有傳遞參數是會報錯的,由於上面定義的 constructor 構造方法中存在參數,因此這裏也必定要傳遞參數
var p = new Person('xiaochuan',22)
alert(p.print())
}
複製代碼
最先接觸單例模式是在學 PHP
的時候,那個時候在尚未使用框架 PHP 引入 Mysql 的時候,我都會把 Mysql 封裝爲一個單例模式的類
單例模式(Singleton),也叫單子模式,是一種經常使用的軟件設計模式。在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統只須要擁有一個的全局對象,這樣有利於咱們協調系統總體的行爲。好比在某個服務器程序中,該服務器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,而後服務進程中的其餘對象再經過這個單例對象獲取這些配置信息。這種方式簡化了在複雜環境下的配置管理
單例模式只容許建立一個對象,所以節省內存,加快對象訪問速度,所以對象須要被公用的場合適合使用,如多個模塊使用同一個數據源鏈接對象等等。如:
一個類能返回對象一個引用(永遠是同一個)和一個得到該實例的方法(必須是靜態方法,一般使用getInstance這個名 稱);當咱們調用這個方法時,若是類持有的引用不爲空就返回這個引用,若是類保持的引用爲空就建立該類的實例並將實例的引用賦予該類保持的引用;同時咱們 還將該類的構造函數定義爲私有方法,這樣其餘處的代碼就沒法經過調用該類的構造函數來實例化該類的對象,只有經過該類提供的靜態方法來獲得該類的惟一實例
# public 公開的
# protected 受保護的
# private 私有的
import http from 'http'
import https from 'https'
import axios, { AxiosResponse, AxiosRequestConfig, CancelTokenStatic } from 'axios'
import { Message, MessageBox } from 'element-ui'
import qs from 'qs'
import { UserModule } from '@/store/modules/user'
// 類名
class Request {
// 屬性
protected baseURL: any = process.env.VUE_APP_BASE_API
protected service: any
protected pending: Array<{
url: string,
cancel: Function
}> = []
protected CancelToken: CancelTokenStatic = axios.CancelToken
protected axiosRequestConfig: AxiosRequestConfig = {}
protected successCode: Array<Number> = [200, 201, 204]
private static _instance: Request;
// 構造函數 初始化工做
private constructor() {
}
// 惟一實例
public static getInstance() : Request {}
protected requestConfig(): void {}
protected interceptorsRequest() {}
protected interceptorsResponse(): void {}
protected removePending(config: any): void {}
public async post(url: string, data: any = {}, config: object = {}) {}
public async delete(url: string, config: object = {}) {}
public async put(url: string, data: any = {}, config: object = {}) {}
public async get(url: string, params: any = {}, config: object = {}) {}
protected requestLog(request: any): void {}
protected responseLog(response: any): void {}
}
export default Request.getInstance()
複製代碼
requestConfig
從名字上咱們就看的出來這是一個關於配置的方法 小提示: void 表示沒有返回值
protected requestConfig(): void {
this.axiosRequestConfig = {
// baseURL`將自動加在 `url` 前面,除非 `url` 是一個絕對 URL
baseURL: this.baseURL,
// `headers` 是即將被髮送的自定義請求頭
headers: {
timestamp: new Date().getTime(),
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
},
// transformRequest` 容許在向服務器發送前,修改請求數據
transformRequest: [function (data: any) {
//對data進行任意轉換處理
return data;
}],
// `transformResponse` 在傳遞給 then/catch 前,容許修改響應數據
transformResponse: [function(data: AxiosResponse) {
return data
}],
// `paramsSerializer` 是一個負責 `params` 序列化的函數
paramsSerializer: function(params: any) {
return qs.stringify(params, { arrayFormat: 'brackets' })
},
// `timeout` 指定請求超時的毫秒數(0 表示無超時時間)
// 若是請求話費了超過 `timeout` 的時間,請求將被中斷
timeout: 30000,
// `withCredentials` 表示跨域請求時是否須要使用憑證
withCredentials: false,
// `responseType` 表示服務器響應的數據類型,能夠是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json',
// `xsrfCookieName` 是用做 xsrf token 的值的cookie的名稱
xsrfCookieName: 'XSRF-TOKEN',
// `xsrfHeaderName` 是承載 xsrf token 的值的 HTTP 頭的名稱
xsrfHeaderName: 'X-XSRF-TOKEN',
// `maxRedirects` 定義在 node.js 中 follow 的最大重定向數目
maxRedirects: 5,
// `maxContentLength` 定義容許的響應內容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定義對於給定的HTTP 響應狀態碼是 resolve 或 reject promise 。若是 `validateStatus` 返回 `true` (或者設置爲 `null` 或 `undefined`),promise 將被 resolve; 不然,promise 將被 rejecte
validateStatus: function(status: number) {
return status >= 200 && status < 300
},
// `httpAgent` 和 `httpsAgent` 分別在 node.js 中用於定義在執行 http 和 https 時使用的自定義代理。容許像這樣配置選項:
// `keepAlive` 默認沒有啓用
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true })
}
}
複製代碼
interceptorsRequest
protected interceptorsRequest() {
this.service.interceptors.request.use(
(config: any) => {
if (UserModule.token) {
config.headers['authorization'] = UserModule.token
}
return config
},
(error: any) => {
return Promise.reject(error)
}
)
}
複製代碼
protected interceptorsResponse(): void {
this.service.interceptors.response.use(
(response: any) => {
if (this.successCode.indexOf(response.status) === -1) {
Message({
message: response.data.message || 'Error',
type: 'error',
duration: 5 * 1000
})
if (response.data.code === 401) {
MessageBox.confirm(
'你已被登出,能夠取消繼續留在該頁面,或者從新登陸',
'肯定登出',
{
confirmButtonText: '從新登陸',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
UserModule.ResetToken()
location.reload()
})
}
return Promise.reject(new Error(response.message || 'Error'))
} else {
return response.data
}
},
(error: any) => {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
}
複製代碼
removePending
protected removePending(config: any): void {
for (let p in this.pending) {
let item: any = p
let list: any = this.pending[p]
if (list.url === `${config.url}/${JSON.stringify(config.data)}&request_type=${config.method}`) {
list.cancel()
this.pending.splice(item, 1)
}
}
}
複製代碼
responseLog
protected responseLog(response: any): void {
if (process.env.NODE_ENV === 'development') {
const randomColor = `rgba(${Math.round(Math.random() * 255)},${Math.round( Math.random() * 255 )},${Math.round(Math.random() * 255)})`
console.log(
'%c┍------------------------------------------------------------------┑',
`color:${randomColor};`
)
console.log('| 請求地址:', response.config.url)
console.log('| 請求參數:', qs.parse(response.config.data))
console.log('| 返回數據:', response.data)
console.log(
'%c┕------------------------------------------------------------------┙',
`color:${randomColor};`
)
}
}
複製代碼
public async post(url: string, data: any = {}, config: object = {}) {
try {
const result = await this.service.post(url, qs.stringify(data), config)
return result.data
} catch (error) {
console.error(error)
}
}
public async delete(url: string, config: object = {}) {
try {
await this.service.delete(url, config)
} catch (error) {
console.error(error)
}
}
...
複製代碼
import http from 'http'
import https from 'https'
import axios, { AxiosResponse, AxiosRequestConfig, CancelTokenStatic } from 'axios'
import { Message, MessageBox } from 'element-ui'
import qs from 'qs'
import { UserModule } from '@/store/modules/user'
class Request {
protected baseURL: any = process.env.VUE_APP_BASE_API
protected service: any = axios
protected pending: Array<{
url: string,
cancel: Function
}> = []
protected CancelToken: CancelTokenStatic = axios.CancelToken
protected axiosRequestConfig: AxiosRequestConfig = {}
protected successCode: Array<Number> = [200, 201, 204]
private static _instance: Request;
constructor() {
this.requestConfig()
this.service = axios.create(this.axiosRequestConfig)
this.interceptorsRequest()
this.interceptorsResponse()
}
public static getInstance() : Request {
// 若是 instance 是一個實例 直接返回, 若是不是 實例化後返回
this._instance || (this._instance = new Request())
return this._instance
}
protected requestConfig(): void {
this.axiosRequestConfig = {
baseURL: this.baseURL,
headers: {
timestamp: new Date().getTime(),
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
},
transformRequest: [obj => qs.stringify(obj)],
transformResponse: [function(data: AxiosResponse) {
return data
}],
paramsSerializer: function(params: any) {
return qs.stringify(params, { arrayFormat: 'brackets' })
},
timeout: 30000,
withCredentials: false,
responseType: 'json',
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxRedirects: 5,
maxContentLength: 2000,
validateStatus: function(status: number) {
return status >= 200 && status < 500
},
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true })
}
}
protected interceptorsRequest() {
this.service.interceptors.request.use(
(config: any) => {
this.removePending(config)
config.CancelToken = new this.CancelToken((c: any) => {
this.pending.push({ url: `${config.url}/${JSON.stringify(config.data)}&request_type=${config.method}`, cancel: c })
})
if (UserModule.token) {
config.headers['authorization'] = UserModule.token
}
this.requestLog(config)
return config
},
(error: any) => {
return Promise.reject(error)
}
)
}
protected interceptorsResponse(): void {
this.service.interceptors.response.use(
(response: any) => {
this.responseLog(response)
this.removePending(response.config)
if (this.successCode.indexOf(response.status) === -1) {
Message({
message: response.data.message || 'Error',
type: 'error',
duration: 5 * 1000
})
if (response.data.code === 401) {
MessageBox.confirm(
'你已被登出,能夠取消繼續留在該頁面,或者從新登陸',
'肯定登出',
{
confirmButtonText: '從新登陸',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
UserModule.ResetToken()
location.reload()
})
}
return Promise.reject(new Error(response.message || 'Error'))
} else {
return response.data
}
},
(error: any) => {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
}
protected removePending(config: any): void {
for (let p in this.pending) {
let item: any = p
let list: any = this.pending[p]
if (list.url === `${config.url}/${JSON.stringify(config.data)}&request_type=${config.method}`) {
list.cancel()
console.log('=====', this.pending)
this.pending.splice(item, 1)
console.log('+++++', this.pending)
}
}
}
public async post(url: string, data: any = {}, config: object = {}) {
try {
const result = await this.service.post(url, qs.stringify(data), config)
return result.data
} catch (error) {
console.error(error)
}
}
public async delete(url: string, config: object = {}) {
try {
await this.service.delete(url, config)
} catch (error) {
console.error(error)
}
}
public async put(url: string, data: any = {}, config: object = {}) {
try {
await this.service.put(url, qs.stringify(data), config)
} catch (error) {
console.error(error)
}
}
public async get(url: string, parmas: any = {}, config: object = {}) {
try {
await this.service.get(url, parmas, config)
} catch (error) {
console.error(error)
}
}
protected requestLog(request: any): void {
}
protected responseLog(response: any): void {
if (process.env.NODE_ENV === 'development') {
const randomColor = `rgba(${Math.round(Math.random() * 255)},${Math.round( Math.random() * 255 )},${Math.round(Math.random() * 255)})`
console.log(
'%c┍------------------------------------------------------------------┑',
`color:${randomColor};`
)
console.log('| 請求地址:', response.config.url)
console.log('| 請求參數:', qs.parse(response.config.data))
console.log('| 返回數據:', response.data)
console.log(
'%c┕------------------------------------------------------------------┙',
`color:${randomColor};`
)
}
}
}
export default Request.getInstance()
複製代碼
import Request from '@/utils/request'
import { ADMIN_LOGIN_API, ADMIN_USER_INFO_API } from '@/api/interface'
interface ILoginData {
username: string
password: string
}
export const login = (params: ILoginData) => Request.post(ADMIN_LOGIN_API, params)
export const getUserInfo = () => Request.get(ADMIN_USER_INFO_API)
複製代碼
各位大哥大姐留個贊吧 O(∩_∩)O哈哈~ 到此就結束了,我也是第一次學習 ts 而且 封裝 axios 寫的很差,下方留言指出,謝謝。