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 { /** * 错误号,200表示成功,10006令牌过期 */ 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 })
大概就是這麼多了
#無痛刷新的原理:當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; /** * 错误号,200表示成功,10006令牌过期 */ 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 }
以上是怎麼使用vue3+ts+axios+pinia實現無感刷新的詳細內容。更多資訊請關注PHP中文網其他相關文章!