在vue3中使用primevue的Toast组件作为全局请求反馈
关于vue3中获取app实例,vue3中使用primevue的toast组件作为request请求反馈
在vue项目中,我们一般会封装一个request.js的请求工具(一般在utils/request.js),然后整个项目的api请求都使用这个封装的request,然后在request中写一些请求的公共配置,比如加密、请求头带上token、反馈请求结果等等
下面是element-admin的request.js文件内容
// request.js
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
效果:(在请求成功或失败时会在右上角反馈信息)
这个模板使用的是vue2 + element-ui
我自己写的时候用的是vue3 + primevue,所有写的时候就遇到了很多问题,在这里记录下
先看primevue里面的Toast用法
main.js
// main.js
import {createApp} from 'vue';
import ToastService from 'primevue/toastservice';
const app = createApp(App);
app.use(ToastService);
<!-- app.vue -->
<script setup>
import Toast from 'primevue/toast';
</script>
<template>
<Toast />
<router-view></router-view>
</template>
<style scoped>
</style>
使用
// 用法1
export default {
mounted() {
this.$toast.add({severity:'success', summary: 'Success Message', detail:'Order submitted', life: 3000});
}
}
// 用法2
import { defineComponent } from "vue";
import { useToast } from "primevue/usetoast";
export default defineComponent({
setup() {
const toast = useToast();
toast.add({severity:'info', summary: 'Info Message', detail:'Message Content', life: 3000});
}
})
可以看出有俩种用法,第一种是通过vue实例获取$toast(需要获取vue实例),第二种是使用useToast方法得到toast(需要在setup中),只要得到了toast就可以调用add方法添加消息了
那么问题就转为了怎么在request.js中获得toast对象了
// request.js
import axios from 'axios'
import store from '@/store'
import { getToken } from '@/utils/auth'
import toast from '@/utils/toast'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
if( res.code !== 200 ) {
if(res.code === 401) {
toast.add({severity:'error',
summary: '未登录',
detail:'请先登录',
life: 3000
});
store.dispatch('user/resetToken').then(() => {
//location.reload()
})
}
return Promise.reject(new Error(res.message))
} else {
return res;
}
},
error => {
console.log('err' + error) // for debug
toast.add({severity:'error',
summary: '错误信息',
detail: error.message,
life: 5000
});
return Promise.reject(error)
}
)
export default service
方式一 useToast(不行)
// toast.js
import { useToast } from "primevue/useToast";
const toast = useToast()
console.log(toast )
export default toast
控制台会直接报错, 可以看出这个方法不可行
方式二 (关键点在于获取vue实例)
在vue2中可以用Vue.prototype获取到toast,而vue3改为了app.config.globalProperties,因此获取到app实例就可以通过app.config.globalProperties.$toast获取到toast了
vue2中可以通过import Vue from 'vue' 直接获取Vue实例,而vue3里面以我的水平还没找到哪个好的接口能直接获取vue实例的
1 用this (不行)
// toast.js
const app = this
const toast = app.config.globalProperties.$toast
export default toast
可以看出this是undefined ,前面的this指的是vue的实例,在普通的组件中可以用,但是在js文件中不能用this
2 getCurrentInstance(不行)
// toast.js
import {getCurrentInstance} from 'vue'
const app = getCurrentInstance()
const toast = app.config.globalProperties.$toast
export default toast
可以看到这里通过getCurrentInstance()获取到的是null
3 导出app
这个方法是我自己想出来的解决方案,vue3的app是通过createApp得到的,我觉得我们可以通过export把他导出来给其他文件使用
// main.js
import {createApp} from 'vue'
import App from './app.vue'
import PrimeVue from 'primevue/config';
import 'primevue/resources/primevue.min.css';
import 'primeicons/primeicons.css'
import 'primevue/resources/themes/saga-blue/theme.css'
import ToastService from 'primevue/toastservice';
const app = createApp(App)
app.use(PrimeVue)
.use(ToastService)
app.mount('#app')
export default app
// toast.js
import app from '@/main'
const toast = app.config.globalProperties.$toast
export default toast
可以看出app是可以拿到的,但是这个时候app还在初始化,会报错,其实我们不需要这么快用app,只是要一个访问他的方法而已,所以可以对他进行封装,等调用的时候才去使用app
// toast.js
import app from '@/main'
getToast = () => app.config.globalProperties.$toast
const toast = {
add(param) {
getToast().add(param)
}
}
export default toast
可以看到当请求失败时页面能够弹出错误提示信息,问题解决,但还是有一个小bug,原因不明
用的vite启动项目是有热部署的,每次修改了当前页面保存时,浏览器会自动更新页面,这时页面会白屏,但是按F5刷新页面又恢复正常,查看控制台
得到这样的信息,经过一系列排查,最终发现是不能import 'main.js',既然这样那就把app的创建放在其他文件里面去,再导出,main.js和其他需要app的文件里面导入app,如下
最终解决方案:
// app.js
import {createApp} from 'vue'
import App from './app.vue'
import PrimeVue from 'primevue/config';
import 'primevue/resources/primevue.min.css';
import 'primeicons/primeicons.css'
import 'primevue/resources/themes/saga-blue/theme.css'
import ToastService from 'primevue/toastservice';
const app = createApp(App)
app.use(PrimeVue)
.use(ToastService)
export default app
// main.js
import app from './app'
app.mount('#app')
// toast.js
import app from '@/app'
const getToast = () => app.config.globalProperties.$toast
const toast = {
add(param) {
getToast().add(param)
}
}
export default toast
// request.js
import axios from 'axios'
import store from '@/store'
import { getToken } from '@/utils/auth'
import toast from '@/utils/toast'
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
if( res.code !== 200 ) {
if(res.code === 401) {
toast.add({severity:'error',
summary: '未登录',
detail:'请先登录',
life: 3000
});
store.dispatch('user/resetToken').then(() => {
//location.reload()
})
}
return Promise.reject(new Error(res.message))
} else {
return res;
}
},
error => {
console.log('err' + error) // for debug
toast.add({severity:'error',
summary: '错误信息',
detail: error.message,
life: 5000
});
return Promise.reject(error)
}
)
export default service
问题全部解决,即使修改当前访问的页面保存,页面也不会出现白屏了。
以上就是本次解决问题的全部过程及思路,技术文笔不好,有错误望大佬指出。
更多推荐
所有评论(0)