赞
踩
vue devtools v6.1.4插件(百度云下载)、chrome拖入即可安装
兼容vue2和vue3,可以在老项目中使用
省略掉mutaions操作,只有state、getters、actions简化了状态管理库的使用,让代码编写更加直观。
不需要嵌套代码,符合vue3的composition Api
完整的支持TypeScript,vuex对TS的支持不完整
代码更加简洁,可以实现很好的代码自动分割。Vue2的时代,写代码需要来回翻滚屏幕屏幕找变量,非常的麻烦,Vue3的Composition api完美了解决这个问题。 可以实现代码自动分割,pinia也同样继承了这个优点。
更简洁的语法,对vue3.0、Composition Api、TypeScript的完整支持。
开发团队就是vuex的开发团队。
vben admin登录的时候,请求的数据是存放在浏览器的本地存储空间(Local Storage)和会话存储空间(Session Storage)的。
点击vue调试面板,选择pinia查看各种Vue状态。pinia同样是一个Vue状态管理工具,兼容vue2和vue3。
src\views\sys\login\LoginForm.vue
定义了登录操作
async function handleLogin() { const data = await validForm(); if (!data) return; try { loading.value = true; const userInfo = await userStore.login({ password: data.password, username: data.account, mode: 'none', //不要默认的错误提示 }); if (userInfo) { notification.success({ message: t('sys.login.loginSuccessTitle'), description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`, duration: 3, }); } } catch (error) { createErrorModal({ title: t('sys.api.errorTip'), content: (error as unknown as Error).message || t('sys.api.networkExceptionMsg'), getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body, }); } finally { loading.value = false; } }
await userStore.login
/** * @description: login */ async login( params: LoginParams & { goHome?: boolean; mode?: ErrorMessageMode; }, ): Promise<GetUserInfoModel | null> { try { const { goHome = true, mode, ...loginParams } = params; const data = await loginApi(loginParams, mode); const { token } = data; // save token this.setToken(token); return this.afterLoginAction(goHome); } catch (error) { return Promise.reject(error); } },
await loginApi
export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') {
return defHttp.post<LoginResultModel>(
{
url: Api.Login,
params,
},
{
errorMessageMode: mode,
},
);
}
defHttp.post(src\utils\http\axios\index.ts),定义了数据处理、请求前后的处理、请求拦截器
export const defHttp = createAxios();
createAxios()
function createAxios(opt?: Partial<CreateAxiosOptions>) { return new VAxios( deepMerge(//deepMerge把参数进行合并 { // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes // authentication schemes,e.g: Bearer // authenticationScheme: 'Bearer', authenticationScheme: '', timeout: 10 * 1000, // 基础接口地址 // baseURL: globSetting.apiUrl, headers: { 'Content-Type': ContentTypeEnum.JSON }, // 如果是form-data格式 // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED }, // 数据处理方式 transform, // 配置项,下面的选项都可以在独立的接口请求中覆盖 requestOptions: { // 默认将prefix 添加到url joinPrefix: true, // 是否返回原生响应头 比如:需要获取响应头时使用该属性 isReturnNativeResponse: false, // 需要对返回数据进行处理 isTransformResponse: true, // post请求的时候添加参数到url joinParamsToUrl: false, // 格式化提交参数时间 formatDate: true, // 消息提示类型 errorMessageMode: 'message', // 接口地址 apiUrl: globSetting.apiUrl, // 接口拼接地址 urlPrefix: urlPrefix, // 是否加入时间戳 joinTime: true, // 忽略重复请求 ignoreCancelToken: true, // 是否携带token withToken: true, }, }, opt || {}, ), ); }
VAxios里面定义了常见的传输方式
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios'; import type { RequestOptions, Result, UploadFileParams } from '/#/axios'; import type { CreateAxiosOptions } from './axiosTransform'; import axios from 'axios'; import qs from 'qs'; import { AxiosCanceler } from './axiosCancel'; import { isFunction } from '/@/utils/is'; import { cloneDeep } from 'lodash-es'; import { ContentTypeEnum } from '/@/enums/httpEnum'; import { RequestEnum } from '/@/enums/httpEnum'; export * from './axiosTransform'; /** * @description: axios module */ export class VAxios { private axiosInstance: AxiosInstance; private readonly options: CreateAxiosOptions; constructor(options: CreateAxiosOptions) { this.options = options; this.axiosInstance = axios.create(options); this.setupInterceptors(); } /** * @description: Create axios instance */ private createAxios(config: CreateAxiosOptions): void { this.axiosInstance = axios.create(config); } private getTransform() { const { transform } = this.options; return transform; } getAxios(): AxiosInstance { return this.axiosInstance; } /** * @description: Reconfigure axios */ configAxios(config: CreateAxiosOptions) { if (!this.axiosInstance) { return; } this.createAxios(config); } /** * @description: Set general header */ setHeader(headers: any): void { if (!this.axiosInstance) { return; } Object.assign(this.axiosInstance.defaults.headers, headers); } /** * @description: Interceptor configuration */ private setupInterceptors() { const transform = this.getTransform(); if (!transform) { return; } const { requestInterceptors, requestInterceptorsCatch, responseInterceptors, responseInterceptorsCatch, } = transform; const axiosCanceler = new AxiosCanceler(); // Request interceptor configuration processing this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => { // If cancel repeat request is turned on, then cancel repeat request is prohibited const { // @ts-ignore headers: { ignoreCancelToken }, } = config; const ignoreCancel = ignoreCancelToken !== undefined ? ignoreCancelToken : this.options.requestOptions?.ignoreCancelToken; !ignoreCancel && axiosCanceler.addPending(config); if (requestInterceptors && isFunction(requestInterceptors)) { config = requestInterceptors(config, this.options); } return config; }, undefined); // Request interceptor error capture requestInterceptorsCatch && isFunction(requestInterceptorsCatch) && this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch); // Response result interceptor processing this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => { res && axiosCanceler.removePending(res.config); if (responseInterceptors && isFunction(responseInterceptors)) { res = responseInterceptors(res); } return res; }, undefined); // Response result interceptor error capture responseInterceptorsCatch && isFunction(responseInterceptorsCatch) && this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch); } /** * @description: File Upload */ uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) { const formData = new window.FormData(); const customFilename = params.name || 'file'; if (params.filename) { formData.append(customFilename, params.file, params.filename); } else { formData.append(customFilename, params.file); } if (params.data) { Object.keys(params.data).forEach((key) => { const value = params.data![key]; if (Array.isArray(value)) { value.forEach((item) => { formData.append(`${key}[]`, item); }); return; } formData.append(key, params.data![key]); }); } return this.axiosInstance.request<T>({ ...config, method: 'POST', data: formData, headers: { 'Content-type': ContentTypeEnum.FORM_DATA, // @ts-ignore ignoreCancelToken: true, }, }); } // support form-data supportFormData(config: AxiosRequestConfig) { const headers = config.headers || this.options.headers; const contentType = headers?.['Content-Type'] || headers?.['content-type']; if ( contentType !== ContentTypeEnum.FORM_URLENCODED || !Reflect.has(config, 'data') || config.method?.toUpperCase() === RequestEnum.GET ) { return config; } return { ...config, data: qs.stringify(config.data, { arrayFormat: 'brackets' }), }; } get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> { return this.request({ ...config, method: 'GET' }, options); } post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> { return this.request({ ...config, method: 'POST' }, options); } put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> { return this.request({ ...config, method: 'PUT' }, options); } delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> { return this.request({ ...config, method: 'DELETE' }, options); } request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> { let conf: CreateAxiosOptions = cloneDeep(config); const transform = this.getTransform(); const { requestOptions } = this.options; const opt: RequestOptions = Object.assign({}, requestOptions, options); const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {}; if (beforeRequestHook && isFunction(beforeRequestHook)) { conf = beforeRequestHook(conf, opt); } conf.requestOptions = opt; conf = this.supportFormData(conf); return new Promise((resolve, reject) => { this.axiosInstance .request<any, AxiosResponse<Result>>(conf) .then((res: AxiosResponse<Result>) => { if (transformRequestHook && isFunction(transformRequestHook)) { try { const ret = transformRequestHook(res, opt); resolve(ret); } catch (err) { reject(err || new Error('request error!')); } return; } resolve(res as unknown as Promise<T>); }) .catch((e: Error | AxiosError) => { if (requestCatchHook && isFunction(requestCatchHook)) { reject(requestCatchHook(e, opt)); return; } if (axios.isAxiosError(e)) { // rewrite error message from axios in here } reject(e); }); }); } }
ContentTypeEnum定义传输类型
export enum ContentTypeEnum {
// json
JSON = 'application/json;charset=UTF-8',
// form-data qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// form-data upload
FORM_DATA = 'multipart/form-data;charset=UTF-8',
}
在src\utils\http\axios\index.ts中定义了请求拦截器,登录成功之后,可以在这里添加token到header中携带给到后端
requestInterceptors: (config, options) => {
// 请求之前处理config
const token = getToken();
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
// jwt token
(config as Recordable).headers.Authorization = options.authenticationScheme
? `${options.authenticationScheme} ${token}`
: token;
//新增我的token
config.headers = { ...config.headers, sd_token: 'haokai' };
}
return config;
},
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。