结合阮一峰老师的es6入门,记录一下自己对于async的使用心得
在用async之前,最好先熟悉Promise,毕竟主流的async都是基于Promise来操作的
async注意事项
1.async函数的返回值是 Promise
2.async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误
3.async函数内部的异步操作执行完,才会执行then方法指定的回调函数
await 注意事项
1.await命令后面是一个 Promise 对象,返回该对象的结果
2.任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行,由此引出try catch 结构
(此块特别重要,故引用官网强调)
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world
以上用法在es6官网吃透,就可以去项目实战,下面说一下我项目用(偷师)到的vue+axios+async+antdUI的网络层架构。
网络层项目架构如下图:
目录说明:
http:封装axios
mock:mockjs
service:整合请求路径,组成具体的网络请求方法
store:调用网络请求,保存请求数据的场所
1.除vuex store外,最外层的index.js负责统一的导入导出,这样的好处是页面引用时都是import相对相同位置的index.js 文件
2.service层中api.js 负责记录请求路径,具体请求方法在各个模块文件夹的index.js中,然后在index.js统一导入导出
3.请求数据保存在vuex中的原因是方便存取,并且可以管理关于网络层的全局状态
然后页面在调用的时候,你只需要这样:
// 调用方法
this.$store.dispatch('demoModule1Module/demoList', {...params})
.then(res=>res)
// 取数据
const data = this.$store.getters['demoModule1Module/demoList']
各模块详情
axios 部分
主流前端网络请求库,想学深入可以去看源码,贴一个实例
import axios from 'axios'
// 请求序列化工具
import qs from 'qs'
// 项目全局配置参数
import { apiBaseUrl, isDebug, accessToken, loginPage } from '@/config/index'
// 统一打印
import Console from '@/utils/Console.js'
// vuex
import store from '@/store/index.js'
// 自定义获取token的方法
import { getAuthToken } from '@/utils/commont.js'
// 错误统一提示
import { message } from 'ant-design-vue'
const NODE_ENV = process.env.NODE_ENV
// console.log(NODE_ENV)
let baseURL = apiBaseUrl.proBaseUrl
if (NODE_ENV === 'development') { // development or production
baseURL = apiBaseUrl.devBaseUrl
}
// 1.创建一个axios的实例
const instance = axios.create({
baseURL, // /category ; /home/data?type=pop&page=1
timeout: 30000 // 30 s
// timeout: 600000 // 10 min
})
// 2.添加默认的配置
// instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
instance.defaults.transformRequest = [function(data, config) {
if (!config['Content-Type']) return qs.stringify(data)
switch (config['Content-Type'].toLowerCase()) {
// 上传json对象
case 'application/json;charset=utf-8': {
return JSON.stringify(data)
}
// multipart/form-data;charset=utf-8 上传文件
case 'multipart/form-data;charset=utf-8': {
return data
}
default: {
// 提交表单
return qs.stringify(data)
}
}
}]
// 3.拦截请求
instance.interceptors.request.use(config => {
store.commit('changeLoading', true)
// 给所有的请求头:统一添加auth_token
if (isDebug) {
Console.log(accessToken)
config.headers.auth_token = accessToken
} else {
config.headers.auth_token = getAuthToken()
}
return config
}, error => {
return Promise.reject(error.data.error.message)
})
// 4.拦截响应 与后台约定处理状态返回在data.code中
instance.interceptors.response.use(response => {
store.commit('changeLoading', false)
Console.log('response=', response)
const data = response.data
if (data) {
if (data.code === 500 || data.code === 1001 || data.code === 403) {
// message.error(data.msg + ',code=' + data.code)
message.error(data.msg)
// 登录过期返回 403
if (data.code === 403) {
// 重定向到指定的url中
if (!isDebug) {
// window.location.href = data.data.url
// window.location.href = loginPage
window.parent.location.href = loginPage
}
}
return response
}
} else {
// message.error('接受的resp.data为空,status=' + response.status)
message.error('接受的resp.data为空')
}
return response
}, error => {
store.commit('changeLoading', false)
Console.log('error=', error)
// const config = error.config
// 判断错误url是否为请求接口*/
if (error.response.status === 404) {
// message.error('请求的url不存在,status=' + error.response.status)
message.error('请求的url不存在')
return Promise.reject(error)
}
// 判断返回的状态码为400
if (error.response.status === 403) {
// message.error('服务禁止请求该资源,status=' + error.response.status)
message.error('服务禁止请求该资源')
return Promise.reject(error)
}
// message.error('网络请求错误error,status=' + error.response.status)
message.error('网络请求错误error')
// 判断返回的状态码为403
return Promise.reject(error)
})
export const axiosInstance = axios
/**
* 封装一个get请求
* @param url // /home?name=xxx&age=xx
* @param params // {name:"keduoli",age:12 }
* @param config // {baseURL:'http://xxxx',timeout: 5000,headers: {'X-Requested-With': 'XMLHttpRequest'}, ... }
* @return promise
*/
export const httpGet = (url, params, config) => {
params = params || {}
config = config || {}
config = Object.assign(config, { params: params })
// Console.log(url, params)
return instance.get(url, config)
}
/**
* 封装一个post请求
* @param url // /home
* @param data // { name:"珂朵莉",age:12 } or FormData
* @param config // {baseURL:'http://xxxx',timeout: 5000,headers: {'X-Requested-With': 'XMLHttpRequest'}, ... }
* @return promise
*/
export const httpPost = (url, data, config) => {
Console.log(url, config)
config = config || {}
return instance.post(url, data, config)
}
统一导入导出index.js
import { axiosInstance as axios, httpGet, httpPost, httpPut, httpDelete } from './axios.js'
export {
axios,
httpGet,
httpPost,
httpPut,
httpDelete
}
service 部分
此块导入操作比较多,需要理清思路,一旦项目变大,这种模式的管理会比较规范且容易管理
这里以user模块为例,介绍一下具体操作
api.js
/**
* =======================================================
* 管理各模块的请求路径 这里面导出的对象会在 service/xx中使用
* =======================================================
*/
export const UserApi = {
// test
login: '/api/4/news/latest'
}
export const demoModule1Api = {
// test
login: '/api/4/news/latest'
}
export const demoModule2Api = {
// test
login: '/api/4/news/latest'
}
user/index.js
import { UserApi } from '@/service/api.js' // 引用api记录的请求路径
import { httpGet } from '@/http/index.js'
/*
*用户中心的服务类( 在service/index.js中统一导出 )
*/
export default class UserService {
/**
* 登录接口(没用到)
* @param {} params 登录提交的参数
*/
async login(params) {
const primose = await httpGet(UserApi.login, params) // httpGet(UserApi.login) 返回的是promise对象
return primose
}
}
最外层 service/index.js
import UserService from '@/service/user/index.js'
import demoModule1Service from '@/service/upload/index.js'
import demoModule2Service from '@/service/upload/index.js'
const userService = new UserService()
const demoModule1Service = new demoModule1Service()
const demoModule2Service = new demoModule2Service()
/**
* 这里导出的service对象,会在对应的store/xxxx/ 的action中导入使用 或者 页面组件中导入使用
*/
export {
userService,
demoModule1Service,
demoModule2Service
}
定义网络请求的具体操作是现在api记录请求路径,然后在模块index的class类下写请求方法然后再到最外层index导出这个class类,具体使用请看vuex层
vuex部分
这一块主要是网络方法的调用与参数传递
使用require.context实现前端工程自动化,自动导入modules
/**
* 自动加载store的二级 modules
*/
const modules = {}
const allModules = require.context('@/store/modules/', true, /index\.(js|ts)$/)
// ["./login/index.js", "./main/books/index.js", "./main/goods/index.js", "./register/index.js"]
console.log('allModules=', allModules.keys())
allModules.keys().forEach((item, index, array) => {
// item = ./login/index.js => login/index.js
// item = ./main/books/index.js => main/books/index.js
const module_path = item.substr(2)
const moduleNames = module_path.split('/') // [login, index.js] - [main, books, index.js]
moduleNames.pop()
const key = moduleNames.join('_')
// const module = require(`@/store/modules/${module_path}`)
const module = allModules(item)
modules[key] = module.default
})
/**
* modules:{
goods :{
// 0.启用命名空间
namespaced: true,
// 1.定义状态
state: {
data: {}, // 列表数据
recordDetail: {} // 详情数据
},
// 2.修改状态
mutations: {
// 这里的 `state` 对象是模块的局部状态
[Types.addData](state, payload) {
state.data = payload
},
[Types.recordDetail](state, payload) {
state.recordDetail = payload
}
},
// 3.提交action,来修改状态
actions: {
async list(context, payload) {
// context 对象 与 store对象有相同的方法;context != store
// 注意:局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
const config = {
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
}
try {
const result = await interrogaterecordService.list(payload, config)
console.log('result=', result)
context.commit(Types.addData, result.data.data)
return Promise.resolve(result.data.data)
} catch (err) {
}
},
async recordDetail(context, payload) {
// context 对象 与 store对象有相同的方法;context != store
// 注意:局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
const config = {
}
try {
const result = await interrogaterecordService.recordDetail(payload, config)
Console.log('recordDetail=', result)
context.commit(Types.recordDetail, result.data.data)
return Promise.resolve(result.data)
} catch (err) {
}
}
},
// 4.获取定义的状态, 通过store.getters获取里面的函数,例如:store.getters.count
getters: {
// state 是获取局部状态;rootState是获取根状态
...ComGetters, // list --> data.content ; listPageConfig -> pageConfig
recordDetail(state, getters, rootState, rootGetters) {
return state.recordDetail || {}
}
}
}
* }
*/
export default modules || {}
顶层store(store/index.js)
import Vue from 'vue'
import Vuex from 'vuex'
import demoModule1Module from './modules/demo/index.js'
import GlobalType from './global-types.js'
Vue.use(Vuex)
export default new Vuex.Store({
// 1.定义状态(提前定义的状态才有响应式的功能)
state: {
count: 0,
// 全局加载进度
loading: false,
},
// 2.修改状态(明确地追踪到状态的变化),通过store.commit('increment')事件触发
mutations: {
increment(state, payload) {
state.count++
},
[GlobalType.changeLoading](state, payload) {
state.loading = payload || false
},
[GlobalType.fileBaseUrl](state, fileBaseUrl) {
state.fileBaseUrl = fileBaseUrl
}
},
// 3.提交action,来修改状态,通过store.dispatch('increment')事件触发
actions: {
increment(context, payload) {
// context 对象 与 store对象有相同的方法;context != store
context.commit('increment')
},
isShowLoading(context, payload) {
// context 对象 与 store对象有相同的方法;context != store
context.commit(GlobalType.changeLoading, payload)
}
},
// 4.获取定义的状态, 通过store.getters获取里面的函数,例如:store.getters.count
getters: {
count(state, getters) {
return state.count
},
loading(state, getters) {
return state.loading
}
},
// 5.将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
modules: {
demoModule1Module, // 测试模块
}
})
子模块(store/demoModule1/index.js)
import demoModuleTypes from './inmates-types.js' // mutations和actions的统一名字映射
import { demoModule1Service } from '@/service/index.js'
import Console from '@/utils/Console.js'
// state 的 demoModule 模块 ( 这个模块在store/index.js 中的module使用 )
const demoModule1Module = {
// 0.启用命名空间
namespaced: true,
// 1.定义状态
state: {
demoData: {}
},
// 2.修改状态
mutations: {
// 这里的 `state` 对象是模块的局部状态
[demoModuleTypes.addDemoData](state, payload) {
state.demoData = payload
}
},
// 3.提交action,来修改状态
actions: {
async demoList(context, payload) {
// context 对象 与 store对象有相同的方法;context != store
// 注意:局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
const config = {
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
}
try {
const result = await demoModule1Service.test(payload, config)
Console.log('result=', result)
context.commit(demoModuleTypes.addDemoData, result.data.data)
return Promise.resolve(result.data.data)
} catch (err) {
// 进行错误处理
}
}
},
// 4.获取定义的状态, 通过store.getters获取里面的函数,例如:store.getters.count
getters: {
// state 是获取局部状态;rootState是获取根状态
demoList(state, getters, rootState, rootGetters) {
return state.demoData.content || []
}
}
}
export default demoModule1Module
配置好vuex层后,网络这块就大功告成了!
完毕
(最近加班到肝疼T_T)