token一定要存在storage缓存中,否则刷新一下,
store会重新被加载,token就没了;
有人问了为什么放在store呢
那存在store是不是多余了,这个也是为了数据统一管理吧,
也是数据可视化,因为缓存中的数据代码中是看不见的
一
分2种情况
单独token一个独立接口
登录接口成功里面存token放置在vuex当中
如下述
单独token一个独立接口
1 手动存储 使用缓存
index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import config from "@/config";
const href21 = `${config.baseUrl22}`;
const store = new Vuex.Store({
state: {
token: [],
},
getters: {
getToken(state, getters) {//getter相当于计算属性 获取state的值
return state.token
}
},
mutations: {// 用于修改 state 里面的数据
saveToken(state, data) {
state.token = data
}
},
actions: {//执行 store.commit 是异步的
askFo({ commit, state }) {
return new Promise((resolve, reject) => {
uni.request({//这个请求接口是uni的 如果写你的记得改下
url: `${href21}?ticket=8a118855753fd59d01756765462c5b9a&type=3`,
method: "GET",
success(res) {
resolve(res.data.data);//这个接口是token (res.data.data的到的是token值)
localStorage.setItem("token", res.data.data);//得到的token在这里本地存储
// uni.setStorageSync("token","res")//这是uni默认存储的方法
commit('saveToken', res)
},
fail(err) {//uni自带的错误返回
reject(err, 1212);
}
})
})
},
}
})
export default store
放请求拦截器里/其他页面调取
在页面的生命周期初始化当中
console.error(localStorage.getItem("token"))
console.error(this.$store.getters.getToken)//得到getter的值
console.error(this.$store.state.token)//得到state的值
2 调用插件 直接写入插件*
这个插件就相当于把缓存直接写好 直接调取state的值就可以了在其他页面当中
使用插件持有化 安装插件
npm i --save vuex-persistedstate
src/store/index.js中引入该插件
import Vue from 'vue'
import Vuex from 'vuex'
//引入持久化插件
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
const store = new Vuex.Store({
//进行配置
plugins: [createPersistedState({
storage: window.sessionStorage, // 默认是 localStorage,这里改成sessionStorage
key:"store", //存储在sessionStorage里面的key名====自定义
reducer: (state) => {
return {
// 要存储的数据:用es6扩展运算符的方式存储了state中所有的数据(缓存全部的state)
...state
// 默认是全部缓存,也可以写成下面这种形式,只缓存需要缓存的
// token: state.token
}
}
})],
state: {
},
})
index.js
import Vue from 'vue'
import Vuex from 'vuex'
//引入持久化插件
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
import config from "@/config";
const href21 = `${config.baseUrl22}`;
const store = new Vuex.Store({
//进行配置
plugins: [createPersistedState({
storage: window.sessionStorage, // 默认是 localStorage,这里改成sessionStorage
key:"store", //存储在sessionStorage里面的key名====自定义
reducer: (state) => {
return {
// 要存储的数据:用es6扩展运算符的方式存储了state中所有的数据(缓存全部的state)
...state
// 默认是全部缓存,也可以写成下面这种形式,只缓存需要缓存的
// token: state.token
}
}
})],
state: {
token: [],
},
getters: {
getToken(state, getters) {//getter相当于计算属性 获取state的值
return state.token
}
},
mutations: {// 用于修改 state 里面的数据
saveToken(state, data) {
state.token = data
}
},
actions: {//执行 store.commit 是异步的
askFo({ commit, state }) {
return new Promise((resolve, reject) => {
uni.request({//这个请求接口是uni的 如果写你的记得改下
url: `${href21}?ticket=8a118855753fd59d01756765462c5b9a&type=3`,
method: "GET",
success(res) {
resolve(res.data.data);//这个接口是token (res.data.data的到的是token值)
commit('saveToken', res)
},
fail(err) {//uni自带的错误返回
reject(err, 1212);
}
})
})
},
}
})
export default store
放请求拦截器里/其他页面调取
//直接这样拿取
console.error(this.$store.state.token)
二
在登录的接口里面的token,登录成功进行获取对应的token,放在vuex里面
- 封装storage是可选的,不封装就用原生的呗;创建store是必须的!
- token一定要存在storage缓存中,否则刷新一下,store会重新被加载,token就没了;
- 那存在store是不是多余了,这个也是为了数据统一管理吧,也是数据可视化,因为缓存中的数据代码中是看不见的~
封装存储storage
封装的存储方法在下方链接
// 存 取 清除 token
set(key, value) {
localStorage.setItem(key, JSON.stringify(value));
// localStorage.key = value;
// localStorage[key] = value;
},
get(key) {
return JSON.parse(localStorage.getItem(key));
},
remove(key) {
localStorage.removeItem(key);
},
创建store
import Vue from 'vue'
import Vuex from 'vuex'
import common from '../utils/common';//这个是引入封装的存储方法
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
token:'',
},
getters:{
getToken(state){
return state.token || common.get("token") || "";
}
},
mutations:{
// 进行赋值state的变量
setToken(state ,token){
state.token = token;
common.set('token', token);
},
// 清空token 登出
delToken(state) {
state.token = "";
common.remove("token");
},
},
actions:{},
modules:{}
})
export default store
创建request
把对应的token放在请求拦截器
判断如果有token添加到请求头当中
放在响应拦截器里面
判断http状态码为401就是没有权限对应跳转到login页面
import axios from 'axios'
import store from '@/store'
import router from '@/router'
import { MessageBox, Loading, Message } from 'element-ui'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 5000
})
let loadingAll;//配置一个loading实例,方便后面使用
let loading_count = 0; //请求计数器
function startLoading() {
if (loading_count == 0) {
// 调用其他loading效果在页面引入放在这个块级里面
loadingAll = Loading.service({
lock: true,
text: "别急,请求加载数据中...",
fullscreen: true,
background: "rgba(0,0,0,0.7)",
spinner: 'el-icon-loading',//自定义加载图标类名
});
}
//请求计数器
loading_count++;
}
function endLoading() {
loading_count--;//只要进入这个函数,计数器就自减,直到。。
if (loading_count <= 0) {
loadingAll.close();
}
}
// 请求拦截器(放token)
service.interceptors.request.use(
config => {
startLoading()//开启loading
/*
判断是否需要token,则放在请求头。
store.getters.getToken;获取的getter值
Authorization是根据后端自定义字段
*/
config.headers.token = store.getters.getToken;
return config;
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器(放提示信息)
service.interceptors.response.use(
response => {
endLoading()//关闭loading
/*
进行判断是否为200 有则提示正确信息,无提示err信息
判断是否200根据返回情况而定
*/
if (response.data.meta.status == '200') {
Message({
message: response.data.meta.msg,
type: 'success',
duration: 5 * 1000
})
} else {
Message({
message: response.data.meta.msg,
type: 'error',
duration: 5 * 1000
})
}
return response.data
},
error => {
endLoading()//关闭loading
//判断错误信息进行提示
if (error && error.response) {
// 1.公共错误处理
// 2.根据响应码具体处理
switch (error.response.status) {
case 400:
error.message = '错误请求'
break;
case 401:
//可能是token过期,清除它
store.commit("delToken");
router.replace({ //跳转到登录页面
path: '/',
// 将跳转的路由path作为参数,登录成功后跳转到该路由
// query: { redirect: router.currentRoute.fullPath }
});
error.message = '未授权,请重新登录'
break;
case 403:
error.message = '拒绝访问'
break;
case 404:
error.message = '请求错误,未找到该资源'
window.location.href = "/NotFound"
break;
case 405:
error.message = '请求方法未允许'
break;
case 408:
error.message = '请求超时'
break;
case 500:
error.message = '服务器端出错'
break;
case 501:
error.message = '网络未实现'
break;
case 502:
error.message = '网络错误'
break;
case 503:
error.message = '服务不可用'
break;
case 504:
error.message = '网络超时'
break;
case 505:
error.message = 'http版本不支持该请求'
break;
default:
error.message = `连接错误${error.response.status}`
}
} else {
// 超时处理
if (JSON.stringify(error).includes('timeout')) {
Message.error('服务器响应超时,请刷新当前页')
}
}
Message.error(error.message)
return Promise.reject(error.response.data)
}
)
export default service
路由router,并设置路由守卫
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '@/store'//引入store必填
import common from '@/utils/common';//引入存储方法必填
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
// 这个不是放home路径home路径单独引入进来
{
path: '/',
name: 'Home',
redirect: '/login',
// 某些页面规定必须登录后才能查看 ,可以在router中配置meta,将需要登录的requireAuth设为true,
},
{ path: '/login', name: 'login', component: () => import('@/views/login/index.vue') },
// 命名不能用特殊字符
{ path: '/home', component: Home },//这个是跳转home的页面主页面
// // 404
// { path: '/404', name: '404', component: () => import('@/views/errPage/404.vue') }
]
const router = new VueRouter({ routes })
// 首先拿到token
if (common.get("token")) {
store.commit("setToken", common.get("token"));
}
// 其次判断是否包含token 必填
router.beforeEach((to, from, next) => {
//判断是否在登录页 是返回
//判断是否不在登录页是否带token不是或者不带token返回登录页
//否则返回放行
if(to.path==='/login') return next();
const tokenStr=store.state.token;
if(!tokenStr){
return next('/login')
}else{
next()
}
})
export default router
页面当中用
登录页面调取接口对应的token拿取到
<template>
<div class="main">
<div class="sec">
<el-form ref="ruleForm" :rules="loginRules" :model="formRef">
<h3>系统登录</h3>
<el-form-item prop="username">
<el-input
ref="username"
prefix-icon="el-icon-user-solid"
v-model="formRef.username"
placeholder="请输入账号"
>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
ref="password"
:type="passwordType"
prefix-icon="el-icon-s-tools"
v-model="formRef.password"
show-password
placeholder="请输入密码"
>
</el-input>
</el-form-item>
<SliderCheck
style="margin: 30px 0"
:successFun="handleSuccessFun"
:errorFun="handleErrorFun"
></SliderCheck>
<el-form-item>
<el-button
type="primary"
v-if="isDisabled"
disabled
style="width: 100%"
>登录</el-button
>
<el-button
type="primary"
v-else
@click="onSubmit('ruleForm')"
style="width: 100%"
>登录</el-button
>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import SliderCheck from "./components/sliderCheck.vue";
import * as math from "@/api/api";
import store from "@/store/index";
export default {
data() {
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error("密码最少6位"));
} else {
callback();
}
};
return {
formRef: {
username: "admin",
password: "123456",
},
passwordType: "password",
iseye: true,
isDisabled: true,
loginRules: {
username: [
{ required: true, message: "请输入用户名称", trigger: "blur" },
],
password: [
{ required: true, trigger: "blur", validator: validatePassword },
],
},
};
},
components: { SliderCheck },
methods: {
// 滑块验证成功回调
handleSuccessFun() {
this.isDisabled = false;
},
// 滑块验证失败回调
handleErrorFun() {
this.isDisabled == true;
},
onSubmit(loginForm) {
//对应于ref的值要一致
this.$refs[loginForm].validate((valid) => {
if (!valid) return; //不符合返回
let params = {
username: this.formRef.username,
password: this.formRef.password,
};
math.getLogin(params).then((res) => {
// 判断username是否有值 有则返回成功进行跳转
if (res.meta.status == '200') {
this.$router.push({
path: "/home", //这个是跳转的页面
query: {
useIndex:res.data,
},
});
}
/*2种1 把请求成功的token存储在vuex中(*)2 把请求成功的token存在sessionStorage*/
this.$store.commit("setToken", res.data.token);//调取getter里面的方法.
});
});
},
},
};
</script>
<style lang="scss" scoped>
</style>