配置request.ts
/** * axios 二次封装 * @auther 何小生。 * @time 2021/08/05 05:24 */ import axios from 'axios' // 引入axios import { config } from '../config' // 引入config import { ElMessage } from 'element-plus' // 引入element-plus import router from '../router' // 由于有些token认证失效等需要用到,所以引入router import { storage } from './storage' // 引入storage,用于获取缓存 // 定义初始化状态码 const TOKEN_INVALID = 'Token认证失败, 请重新登录' const NETWORK_ERROR = '网络请求异常, 请稍后重试' // 创建axios实例对象, 添加全局配置 const service = axios.create({ // 初始配置请求头 当环境为mock的时候,请求mockapi,否则请求正式的api baseURL: config.mock ? config.mockApi : config.baseApi, // 接口持续时间为8秒,否则超时 timeout: 8000 }) // 请求拦截 service.interceptors.request.use((req) => { // TO-DO // 获取请求头 const headers = req.headers // 获取token 由于是typescript,所以要做排斥赋值 const { token = "" } = storage.get('userInfo') || {} // 跟后端定义的某个请求头的值用于解析token身份令牌 if(!headers.Authorization) headers.Authorization = 'xiaohe ' + token // 返回请求头 return req }) // 响应拦截 service.interceptors.response.use((res) => { // 获取后端返回的code,data和提示语 const { code, data, msg } = res.data if(code == 200) return data else if(code === 50001) { // token认证失败 ElMessage.error(TOKEN_INVALID) // 给予5001的状态码 // 并且给予用户 一定的反应时间后,跳转登录页 setTimeout(() => { router.push('/login') }, 15000) return Promise.reject(TOKEN_INVALID) // 抛出异常 } else { // 丢出服务器异常 ElMessage.error(msg || NETWORK_ERROR) return Promise.reject(msg || NETWORK_ERROR) } }) /** * @param {*} options 请求配置 */ function request(options: any) { options.method = options.method || 'get' if(options.method.toLowerCase() === 'get') options.params = options.data if(typeof options.mock != 'undefined') config.mock = options.mock if(config.env === 'prod') service.defaults.baseURL = config.baseApi else service.defaults.baseURL = config.mock ? config.mockApi : config.baseApi return service(options) } // 轮询接口类型,然后根据对应的类型,给予请求方式 ['get', 'post', 'put', 'delete', 'patch'].forEach(item => { request[item] = (url: string, data: any, options: string[]) => { return request({ url, data, method: item, ...options }) } }) // 丢出request export default request 复制代码
封装storage.ts
此处用到了storage和sessionStorage两种方法做缓存封装
/** * 封装操作localstorage本地存储的方法 * @auther 何小玍。 * @date 2021/06/28 */ export const storage = { //存储 set(key: string, value: any) { window.localStorage.setItem(key, JSON.stringify(value)) }, //取出数据 get<T>(key: string) { const value = window.localStorage.getItem(key) if (value && value != "undefined" && value != "null") return <T>JSON.parse(value) else return "{}" }, // 删除数据 remove(key: string) { window.localStorage.removeItem(key) } }; /** * 封装操作sessionStorage本地存储的方法 */ export const sessionStorage = { //存储 set(key: string, value: any) { window.sessionStorage.setItem(key, JSON.stringify(value)) }, //取出数据 get<T>(key: string) { const value = window.sessionStorage.getItem(key); if (value && value != "undefined" && value != "null") return JSON.parse(value) return null }, // 删除数据 remove(key: string) { window.sessionStorage.removeItem(key) } } 复制代码
配置config.ts
在src目录下创建config文件夹,然后创建index.ts, 用于配置请求的基本配置参数和区分生产环境和开发环境
export interface IConfig { env: string // 开发环境 mock?: boolean // mock数据 title: string // 项目title baseApi?: string // api请求地址 mockApi?: string // mock地址 } const dev: IConfig = { env: "development", mock: false, title: "开发", baseApi: "/api", // 本地api请求地址,注意:如果你使用了代理,请设置成'/' mockApi: "https://www.fastmock.site/mock/4f8c864d98d32e623e4a452a904ca70b/api" } const prod: IConfig = { env: "production", mock: false, title: "生产", baseApi: "https://www.baidu.com/api", // 正式api请求地址 mockApi: 'xxx' } export const config: IConfig = import.meta.env.MODE == 'development' ? dev : prod 复制代码
配置api封装
在src目录下,创建api文件夹,然后生成user.ts文件,存放登录注册忘记密码等接口
import request from '../utils/request' interface userState { username: string password: string } export default { /** * 登录接口 * @param { string } username 用户名称 * @param { string } password 用户密码 */ login( data: userState ) { return request({ url: '/users/login', method: 'post', data }) } } 复制代码
配置router和路由守卫
在src文件夹下创建index.ts、router.config.ts
- 路由守卫的配置
import { createRouter, createWebHistory } from "vue-router" import { constantRouterMap } from "./router.config" import { useDocumentTitle } from "@/hooks/useDocumentTitle" import store from "@/store" const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), // 在按下 后退/前进 按钮时,就会像浏览器的原生表现那样 scrollBehavior(to, from, savedPosition) { if (savedPosition) return savedPosition else return { top: 0 } }, routes: constantRouterMap }) // 路由开始进入 router.beforeEach((to: any, from: any, next) => { useDocumentTitle(to.meta.title) next() return false }) router.afterEach((to, from, next) => { // 保存url }) export default router 复制代码
- 路由配置
import { RouteRecordRaw } from "./vue-router" import Layout from '@/layout/index.vue' export const constantRouterMap: Array<RouteRecordRaw> = [ { path: '/login', name: 'login', component: () => import('@/views/login/login.vue'), meta: { title: '登录' }, hidden: true }, { path: '/', name: '/', component: Layout, redirect: '/index', meta: { title: '博客', icon: 'el-icon-help' }, children: [ { path: '/index', name: 'index', component: () => import('@/views/index/index.vue'), meta: { title: '博客', icon: 'el-icon-link' } } ] }, { path: '/404', name: 'page404', component: () => import('@/views/404.vue'), meta: { title: '404' }, hidden: true }, { path: '/:catchAll(.*)', redirect: '/404', hidden: true } ] 复制代码
main.js配置
最后在main.js里面配置
import { createApp } from 'vue' import App from './App.vue' import router from './router' import store from './store' import svgIcon from './icons/index.vue' import { storage, sessionStorage } from './utils/storage' import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' // 引入全局样式 import "./styles/base.css" import "./styles/reset.css" const app = createApp(App) app.config.globalProperties.storage = storage // 全局挂载 缓存方法 app.config.globalProperties.sessionStorage = sessionStorage // 全局挂载 缓存方法 app .use(router) .use(store) .use(ElementPlus) .component('svg-icon', svgIcon) .mount('#app')