目录
1.辅助函数
2.登录后,刷新用户状态不消失的实现
3.自定义存储工具API
4.只有在登录之后,才可以看到除登录之外的页面(全局前置守卫)
5.只有在登录之后,才可以发送其他的http增删改查的请求
6.总结
1.vue辅助函数
知识点:
vue的辅助函数有:1、mapState,将全局状态管理的state值映射到组件的计算属性;2、mapGetters,将全局状态管理的getters值映射到组件的计算属性;3、mapActions;4、mapMutations。
Vuex提供了一些辅助函数,可以简化访问Store中数据的代码:
computed: state getter
methods: mutations actions
import {mapState, mapMutations, mapActions} from 'vuex'
.....
data(){
return {
user: ''
}
},
computed: {
...mapState(['user', 'token']),
...mapGetters(['....'])
},
methods: {
...mapMutations(['updateUser', 'saveToken'])
...mapActions(['login'])
}
使用辅助函数前:
this.$store.state.user.nickname
使用了辅助函数后:
user.nickname
// 状态: vuex的单一状态树,用于存储全局共享的数据信息。
state: {
// 该user对象就是存储在vuex中的可以供组件直接访问的全局状态信息
user: null,
city: '北京',
token: 'tokenxxxxx',
data: []
},
Homeview 全部简写 去掉this.$store.state.user.xxxxxx
知识点:computed method 的区别
1.computed是响应式的,methods并非响应式。
2.调用方式不一样,computed定义的成员是像属性一样访问,methods定义的成员必须以函数形式调用。
3.computed是带缓存的,只有依赖数据发生改变,才会重新进行计算,而methods里的函数在每次调用时都要执行。
<!-- 显示用户信息 -->
<div v-if="$store.state.user">
<div v-if="user">
<i class="el-icon-user"></i>
<span>{{user.nickname}}</span>
<i class="el-icon-phone-outline"></i>
<span>{{user.phone}}</span>
<i class="el-icon-chat-dot-round"></i>
<span>{{user.email}}</span>
</div>
</el-header>
<el-main>
<script>
import {mapState, mapMutations} from 'vuex' // 辅助函数
export default {
data() {
return {
isCollapse: false
}
},
// 如下写法, this.user 就是 vuex中state里的user
// computed: mapState(['user', 'token'])
computed: {
...mapState(['user', 'token']),
getTotal(){
return 100
}
},
methods: {
// 以下写法相当于将vuex的mutations中声明的updateUser方法引入
// 调用this.updateUser(参数对象),即可执行vuex中的mutations
...mapMutations(['updateUser'])
},
};
</script>
2.登录后,刷新用户状态不消失的实现
实现思路:
-
当登录成功,向vuex中存储用户对象,同时也需要向sessionStorage中存一份。
-
当vuex初始化时,直接从sessionStorage中加载初始化数据。例子:
state:{ user: sessionStorage.getItem(xx) }
修改store/index.js state
state: { // 该user对象就是存储在vuex中的可以供组件直接访问的全局状态信息 // user: sessionStorage.getItem('USER')?JSON.parse(sessionStorage.getItem('USER')): null,
-
3.自定义存储工具API:
新建utils/Storage .js
export const KEYS = { TOKEN:'TOKEN', USER_INFO : 'USER_INFO', CITY_NAME :'CITY_NAME' } export const set = (key,data)=>{ sessionStorage.setItem(key,JSON.stringify(data)) } export const get = (key)=>{ let data = sessionStorage.getItem(key) if(data){ return JSON.parse(data) }else{ return null } }
import {KEYS, set, get} from '@/utils/Storage' state: { // 该user对象就是存储在vuex中的可以供组件直接访问的全局状态信息 user: get(KEYS.USER_INFO), },
import {KEYS, set, get} from '@/utils/Storage' // 存数据 set(KEYS.USER_INFO, res.data.data.user)
4.只有在登录之后,才可以看到除登录之外的页面(全局前置守卫)
**业务需求:**若已经登录,则可以访问普通页面;若没有登录的前提下访问普通页面,应直接跳转到登录页面。
实现思路:在项目的VueRouter对象中添加路由的全局前置守卫。前置守卫将会在路由跳转到目标页之前执行,那么就可以在此处进行判断用户的登录状态。
1.若已经登录,则可以访问普通页面。
2.若没有登录的前提下访问普通页面,应直接跳转到登录页面。
// 全局的前置路由守卫,将会在每一次路由跳转时执行(目标显示之前)
router.beforeEach((to, from, next)=>{
console.log('from:', from)
console.log('to:', to)
next() // 调用next()方法才可以继续原本的跳转业务流程
})
每个守卫方法接收三个参数:
- to: Route: 即将要进入的目标 路由对象
- from: Route: 当前导航正要离开的路由
- next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖
next
方法的调用参数。
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave
守卫。 - 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter
守卫中传给next
的回调函数,创建好的组件实例会作为回调函数的参数传入。
5.只有在登录之后,才可以发送其他的http增删改查的请求
如果想要实现该功能,则需要服务端进行用户验证(鉴权),验证当前请求中是否包含用户的身份信息。如果该用户有访问该资源的权限,则执行相应业务;如果用户没有处理请求的权限,则直接驳回,返回错误消息:您没有权限。
实现思路:
登录成功后,将token字符串保存下来。Vuex+Storage。
以后每次发请求,都应该修改axios的代码,添加一个消息头。带着authorization消息头一起发送请求即可。
此处可以借助axios的请求拦截器来实现,统一添加消息头。
mutations: {
saveToken(state,token){
state.token = token
},
/** 此处声明方法,该方法将被vuex所管理
* 方法用于修改state中的用户数据,如果需要修改用户数据。
* state: vuex自动传入的state对象
* payload: 用户调用当前方法时传递的自定义参数
*/
updateUser(state, payload){
state.user = payload
}
},
actions: {
/**
* 执行异步登录业务,登录成功后将用户对象存入state(调用mutations)
* @param store vuex自动传入 Store对象
* @param params 用户传递的参数 {username:xx, password:xx}
*/
login(store, params){
httpApi.adminApi.login(params).then(res=>{
if(res.data.code==200){ // 登录成功
store.commit('updateUser', res.data.data.user)
store.commit('saveToken', res.data.data.token)
//
set(KEYS.USER_INFO,res.data.data.user)
set(KEYS.TOKEN,res.data.data.token)
Notification.success({
message: '登录成功'
})
router.replace('/home/index')
}else { // 登录失败
Notification.error({ message:res.data.msg })
}
})
}
},
modules: {
}
})
MyAxios 添加请求拦截器,对config进行操作
const instance = axios.create()
//添加请求拦截器
instance.interceptors.request.use(function(config){
//对config进行操作
let token =store.state.token
if(token){
config.headers['Authorization'] = token
}
return config;
})
bmdstudios-server 修改index.js
// 测试环境中,不做token拦截,直接执行后续业务
// if(1==1){
// next();
// return;
// }
// 执行token验证
let token = req.headers["authorization"];
try {
let payload = jwt.verify(token, JWT_SECRET_KEY);
req.tokenPayload = payload // 将token中存储的数据,直接复制给req,这样在后续业务中就可以使用req.tokenPayload获取这些信息
} catch (error) {
resp.send(Response.error(401, '用户验证失败,请重新登录'))
return;
}
next(); // 继续后续业务的执行
};
6.总结
-
什么时候用vuex?
-
怎么用?
虽然vuex很好用,可以方便的实现SPA单页面应用的状态数据的统一集中管理,但是不能刷新,一旦用户刷新了浏览器,就会重新加载整个vue环境,vuex中的数据也将会初始化。
所以在使用vuex存储数据时,都需要思考一个问题:这些数据是否需要持久化存储?
如果需要,则应借助于html5提供的sessionStorage与localStorage来完成。当向vuex中存储数据时,还需要向webStorage存一份(这个地方可以长久存储),当用户f5刷新页面时,vuex中数据可以从webstorage中读取并初始化。
3.为什么不直接使用Storage存储数据呢?能不能就不用vuex了?
Storage虽然有持久化存储的特点,但是存储在Storage中的数据没有响应式的特点。如果修改了Storage中的数据,UI是不会及时更新的。所以一般情况下,都需要二者配合在一起使用,配合的方式都雷同:
- 存的时候,顺便 存入storage。
- vuex的state中初始化时,要从storage中加载并初始化。
发现了一个大bug,好像不登录也能进行数据的增删改查。 实际上,应实现如下业务场景:
- 只有在登录之后,才可以看到除登录之外的页面。
- 只有在登录之后,才可以发送其他的http增删改查的请求,否则发送的所有请求都应该被服务端驳回:不登录就没有操作数据的权限。
知识点:
HTTP状态管理方式
短连接通信模式:客户端与服务端建立连接,通信完毕后,连接断开。
长连接通信模式:客户端与服务端建立连接,通信完毕后,连接不断开。
由于HTTP请求属于短连接模式的请求,所以每个http请求都是独立的,互不相关。所以http协议也称为“无状态的通信协议”。
http的状态管理:将同一个客户端的多次请求当做一个整体来看待;将同一个客户端发送请求时涉及到的状态数据保存下来,每次发请求时要让服务端知道。
基于Token机制实现http状态管理
当登录成功后,服务端将会返回登录用信息,并且还会返回一个token字符串。
客户端保存这些数据。user token。
以后同一个客户端发送后续所有请求时,都需要携带该token字符串一起发送请求。这样服务端才知道该客户端的登录信息,用户拥有什么权限。因为所有的信息都在这个加了密的token字符串里,服务端可以对该字符串进行解密,从而获取上一次保存的信息,完成相应业务。