vue3+ts+axios+pinia实现无感刷新
1.先在项目中下载aiXos和pinia
npm i pinia --save
npm install axios --save
2.封装axios请求-----
下载js-cookie
npm i JS-cookie -s
//引入aixos
import type { AxiosRequestConfig, AxiosResponse } from "axios";
import axios from 'axios';
import { ElMessage } from 'element-plus';
import { useUserInfoStore } from '@/stores/modules/UserInfo'
import router from '@/router';
import qs from 'qs';
import Cookie from "js-cookie";
let baseURL = "";
// console.log(process.env.NODE_ENV);//判断环境
if (process.env.NODE_ENV === 'development') {
baseURL = '/m.api'//后台请求接口地址
} else {
baseURL = 'http://xxxx.cn:8080';//这里是项目上线后的地址
}
declare interface TypeResponse extends AxiosResponse {
errno: number,
errmsg: string
}
//创建axios实例
const instance = axios.create({
baseURL, // 接口地址
timeout: 3000,
headers: {
"Content-Type": 'application/x-www-form-urlencoded'
}
});
//添加拦截器
instance.interceptors.request.use((config) => {
// 在发送请求之前做些什么--给请求头添加令牌token
(config as any).headers['AdminToken'] = Cookie.get('token')//从cookie中拿token值,
//这里是使用了js-cookie插件。
// console.log(config, "请求拦截器")
return config
}, reeor => {
// 对请求错误做些什么
return Promise.reject(reeor);
});
// 需要无痛刷新的操作页面
const METHOD_TYPE = ["_mt=edit", "_mt=create", "_mt=delete"]
// //响应拦截器
instance.interceptors.response.use(async (response: AxiosResponse) => {
// 对响应数据做点什么
let data = response.data;
let { errno, errmsg } = data;
console.log(response, "响应拦截器");
let path = router.currentRoute.value.fullPath;//当前路由路径
if (10006 === errno) {
const configData = response.config.data || ''
// 判断请求类型是否需要无痛刷新,index !== -1则需要无痛刷新
let index = METHOD_TYPE.findIndex(item => configData.includes(item))
if (-1 === index) {//需要重新登入获取令牌
router.replace({ path: "/login", query: { back: path } })//登入后需要跳回的地址
return
} else {//需要无痛刷新令牌
const store = useUserInfoStore();
const { username, password } = store.LoginInfo//在状态管理里面定义一个loginInfo
// 1.重新获取令牌
let loginData = { _gp: 'admin', _mt: 'login', username, password };
const { errno, errmsg, data } = await post(loginData)//这里是通过async 将异步序列化改为同步
if (200 == errno) {
Cookie.set('token', data)//保存令牌
} else {
router.replace({ path: "/login", query: { back: path } })//登入后需要跳回的地址
return Promise.reject({ errno, errmsg, data })
}
return instance.request(response.config)
}
// ElMessage.error(errmsg);//错误信息
}
return data;
}, reeor => {
console.log(reeor);
return Promise.reject(reeor);
})
function get(params?: object): Promise<TypeResponse> {
return instance.get('', { params });
};
function post(data: object, params?: object): Promise<TypeResponse> {
return instance.post('', qs.stringify(data), { params });
};
//暴露实列
export {
post, get,
}
3.qs.stringify(data)是将请求的数据转成表单格式,如果不需要直接去掉就可以了;
4.重新登录后跳转路由需要设置,不需要可以去掉
5。状态管理--数据
下载持久化工具
npm install pinia-plugin-persistedstate --s
在main.js中配置持久化
//引入数据持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate);
app.use(pinia)
import { defineStore } from 'pinia'
export const useUserInfoStore = defineStore('UserInfo', {
state:() => ({
LoginInfo:{
username:'',
password:''
}
}),
persist:true;//状态存储持久化
})
6.登录页面--存储表单数据,也就是用户名和密码
npm i lodash --s
import Cookies from 'js-cookie';//引入cookie
import * as _ from 'lodash';//防抖节流插件
import {post} from '@/util';
import {useUserInfoStore} from '@/stores/modules/UserInfo' ;//用户信息
import { useRouter,useRoute } from 'vue-router' ;//引入路由
//这里是表单输入的数据
const ruleForm = reactive({
password: '123456',
username: 'admin'
});
//请求接口数据
let data = {
_gp: "admin",
_mt: 'login',
...ruleForm
};
let LoginInfo = useUserInfoStore().LoginInfo;//状态管理定义的数据
async function init() {
await post(data).then((res:any) => {
let { data: token, errno, errmsg } = res
if (200 === errno) {
let time = new Date() //设置过期时间
time.setTime(time.getTime() + 1000 * 60 * 30)
Cookies.set('token', token, { expires: time });
Object.assign(LoginInfo,ruleForm)
if (route.query.back) { //如果存在参数
let paths = route.query.back+''//拼接字符串
console.log(paths);
if (paths=='/') {
//因为我的home是'/',所有需要判断
router.replace('/Surface')//跳转至主页
return
}else{
router.replace(paths)//则跳转至进入登录页前的路由
}
} else {
router.replace('/Surface')//否则跳转至首页
}
} else {
ElMessage.error(errmsg)
}
}).catch((err:any) => {
ElMessage.error('登录异常!')
})
let info = {//用户信息请求信息接口数据
_gp: "admin",
_mt: 'info',
}
//下面这个函数是请求用户信息的,不需要可以不写
await post(info).then((res:any) => {
let {data} = res
console.log(data);
infos(data)
}).catch((err:any)=>{
ElMessage.error('登录异常!')
})
}
//防抖节流
const fangdou = _.debounce(init, 1500, {
leading: true, // 延长开始后调用
trailing: false // 延长结束前调用
})
//移除组件时,取消防抖
onUnmounted(() => {
fangdou.cancel()
})
7.main.js设置路由守卫
import Cookie from 'js-cookie'
import router from './router'//引入路由
//路由守卫
router.beforeEach(async (to, from ) => {
let tokent:string|undefined = Cookie.get('token')
if (!tokent && to.path == '/login') {
return true
}
// 没有登录,强制跳转登录页
if (!tokent && to.path !== '/login') {
router.replace({path:'/login',query:{back:to.path}});
}
// 防止重复登录
if (tokent && to.path === '/login') {
return {
path: from.path ? from.path : '/Surface'
}
}
return true
})
大概就是这么多了
vue3无痛刷新(无感刷新)
无痛刷新的原理:当token过期后,在响应拦截器通过判断重新进行登入请求
实现过程
在状态管理state中定义一个loginInfo对象用于存储用户的账号和密码
//在状态管理文件中
import { defineStore } from 'pinia'
import Cookies from "js.cookie"
import {post} from '@/http'
import router from '@/router';
import { ElMessage } from 'element-plus';
export const useUserStore = defineStore({
id: "userStore",
persist: {
paths:['user']
},//持久化
state: () => ({
loginInfo:{
username:'',
password:''
}
}),
getters: {},
actions: {
setUser(user:UserInfo) {
this.user = user;
},
loginOut(){
const data ={
_gp:"admin",
_mt:"logout"
}
post({},data).then((res:any)=>{
let {errmsg,errno} = res
if(200==errno){
localStorage.removeItem("userStore")
Cookies.remove("token")
router.replace('/login')
ElMessage.success(errmsg);
}else{
ElMessage.error(errmsg);
}
}).catch(res=>{
console.log(res,"退出登入失败");
})
}
}
})
登入页面中,在登入请求成功后将用户的账号和密码存储在状态管理
//在登入页面文件中
const data = { ...ruleForm, _gp: "admin", _mt: "login" }//转换成字符串
post({}, data).then(res => {
let { data: token, errmsg, errno } = res as any;//获取登录状态
if (200 == errno) {//登录成功的判断
ElMessage.success("登录成功!")//消息提示登录成功
let now = new Date();//获取当前时间
now.setTime(now.getTime() + 1000 * 60 * 30);//转成时间类型
Cookies.set("token", res.data, { expires: now })//获取token存到cookie
Object.assign(store.loginInfo, ruleForm)//将账号密码存储到状态管理
return Promise.resolve(token)
} else {
ElMessage.error(errmsg);//登入失败
return Promise.reject(errmsg)
}
})
3.在http中,先定义一个数组变量,该数组存放需要无痛刷新的操作如:删除、添加、编辑
4.在响应拦截器中,判断10006是否等于errno,如果相等说明令牌过期了,否则没过期
5.令牌过期,获取接口请求数据在定义的数组中查找判断请求类型是否需要无痛刷新
6.index===-1则表示在数组中没有找到也就不需要无痛刷新,直接跳到登入页面进行登入
7.index!==-1则表示需要无痛刷新,将状态管理中存储的用户账号和密码解构出来,进行登入接口请求从而达到重新获取令牌,进而达到不需要进入登入页面就可以进行登入请求也就达到无痛刷新的效果
//在封装的http文件中
import axios, { type AxiosResponse } from 'axios';
import qs from 'qs'
import Cookies from "js.cookie"
import router from '@/router';
import { ElMessage } from 'element-plus';
import { useUserStore } from '@/stores/admin';
declare interface TypeResponse extends AxiosResponse {
url(url: any): unknown;
[x: string]: any;
errno: number,
error: string,
errmsg: string
}
let baseURL = ''
if (process.env.NODE_ENV === "development") {
baseURL = '/m.api'
} else {
baseURL = "http://zxwyit.cn:8080/m.api"//上线后的路径
}
const instance = axios.create({//创建实例
baseURL,
headers: { "content-type": "application/x-www-form-urlencoded" }
})
// 请求拦截器
instance.interceptors.request.use(function (config) {
if (config.headers) {
config.headers['AdminToken'] = Cookies.get("token") + ''
}
return config
}, function (error) {
console.error('请求错误', error)
return Promise.reject(error)
}
)
// 无痛刷新的原理:当token过期后,在响应拦截器通过判断重新进行登入请求
// 实现过程:
// 1.在状态管理state中定义一个loginInfo对象用于存储用户的账号和密码
// 2.登入页面中,在登入请求成功后将用户的账号和密码存储在状态管理
// 3.在http中,先定义一个数组变量,该数组存放需要无痛刷新的操作如:删除、添加、编辑
// 4.在响应拦截器中,判断10006是否等于errno,如果相等说明令牌过期了,否则没过期
// 5.令牌过期,获取接口请求数据在定义的数组中查找判断请求类型是否需要无痛刷新
// 6.index===-1则表示在数组中没有找到也就不需要无痛刷新,直接跳到登入页面进行登入
// 7.index!==-1则表示需要无痛刷新,将状态管理中存储的用户账号和密码解构出来,
// 进行登入接口请求从而达到重新获取令牌,进而达到不需要进入登入页面就可以进行登入请求也就达到无痛刷新的效果
// 需要无痛刷新的操作页面
const METHOD_TYPE = ["_mt=edit", "_mt=create", "_mt=delete"]
// 响应拦截器
instance.interceptors.response.use(async function (response) {
let data = response.data//强解
let { errno, errmsg } = data//结构赋值
let path = router.currentRoute.value.fullPath//获取路径
console.log(errno,'errno');
if (10006 == errno) {
// 获取接口请求数据
const configData = response.config.data || ''
// 判断请求类型是否需要无痛刷新,index !== -1则需要无痛刷新
let index = METHOD_TYPE.findIndex(item => configData.includes(item))
if (-1 === index) {//需要重新登入获取令牌
router.replace({ path: "/login", query: { back: path } })//登入后需要跳回的地址
return
} else {//需要无痛刷新令牌
const store = useUserStore();
const { username, password } = store.loginInfo//在状态管理里面定义一个loginInfo
// 1.重新获取令牌
let loginData = { _gp: 'admin', _mt: 'login', username, password };
const { errno, errmsg, data } = await post(loginData)//这里是通过async 将异步序列化改为同步
if (200 == errno) {
Cookies.set('token', data)//保存令牌
} else {
console.log(55);
router.replace({ path: "/login", query: { back: path } })//登入后需要跳回的地址
return Promise.reject({ errno, errmsg,data})
}
return instance.request(response.config)
}
}
return data
}, function (error) {
console.error('响应错误', error)
return Promise.reject(error)
}
)
function get(params?: object): Promise<TypeResponse> {
return instance.get("", { params })
}
function post(data: object, params?: object): Promise<TypeResponse> {
return instance.post("", qs.stringify(data), { params })
}
export function upload(data: object): Promise<TypeResponse> {
return instance.post("http://192.168.1.188:8080/upload/admin", data);
}
export { get, post }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。