问题
I would like to auto-sign-in user when the page has been refreshed. I've read that I should use vuex-persistedstate to persist the token in localstorage. Here's my vuex store:
store: {
user: null
},
actions: {
autoSignIn ({commit}, payload) {
commit('setUser', { id: payload.token })
}
},
mutations: {
setUser (state, payload) {
state.user = payload;
}
},
plugins: [ createPersistedState({
getState: (key) => localStorage.getItem(key),
setState: (key, state) => localStorage.setItem('user_token', key)
}) ]
I also have signIn action where I create a newUser with token.
signUserIn ({commit, getters, state}, payload) {
let data = {
_username: payload.email,
_password: payload.password
}
Vue.http.post(
'url',
data,
{ channel: 'default' },
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
).then(response => {
const newUser = {
id: response.body.token
}
localStorage.setItem('user_token', response.body.token)
commit('setUser', newUser)
})
}
Then in main.js - created() I would like to check if the token is valid, afterwards - sign user in.
created() {
let token = localStorage.getItem('user_token')
if(token) {
this.$store.dispatch('autoSignIn', token)
}
}
The last part doesn't work, I know I should use getState, setState from createPersistedState but I have no idea how to do it. How do I make it work?
回答1:
If the only use case for using vuex-persistedstate is to remember the access token then you should avoid using it in the first place and save yourself a few Kb from the final build file.
It would make more sense using it if you were to provide offline experience to your users.
If all you do is set state.user with the locally stored token then you could just do.
// if localStorage contains a serialized object with a 'token' attribute
const userToken = JSON.parse(window.localStorage.getItem('user_token'));
const state = {
user: userToken ? userToken.token || null : null
};
const mutations = {};
const actions = {};
export default {
state,
mutations,
actions,
}
Whenever you refresh the page and the store is being instantiated state.user will either take as default value the locally stored token or null if missing/undefined
However if i were you i would replace
const state = {
user: null
};
with
const state = {
accessToken: null
};
since all you store is the accessToken and not the user itself so its kind misleading.
update to answer the question in comments "... I need to check if the state has changed and use setUser mutation but don't how to achieve it."
There are 3 ways I can think of.
first of all change state to
const userToken = JSON.parse(window.localStorage.getItem('user_token'));
const state = {
accessToken: userToken ? userToken.token || null : null,
user: null,
};
then
The Simplest of all
on your App.vue component add a mounted method like the following
import { mapState, mapActions } from 'vuex';
export default {
...
computed: {
...mapState([
'accessToken',
'user',
])
},
mounted() {
if (this.accessToken && !this.user)
this.getAuthUser();
},
methods: {
...mapActions([
'getAuthUser',
]),
},
}
So on every refresh when the App is mounted and we have an accessToken but not a user we call getAuthUser() action which makes an ajax call and stores the received user with a setUser mutation
The Router Guard way
If you have a router and you only need to check for an authenticated user on certain routes then you can use route guards. for example
import store from '@/store';
export default new Router({
routes: [
...
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (!store.state.accessToken) return next('/login');
if (store.state.accessToken && !store.state.user) {
return store.dispatch('getAuthUser')
.then(() => {
// user was retrieved and stored and
// we can proceed
next();
})
.catch(() => {
// we couldn't fetch the user maybe because the token
// has expired.
// We clear the token
store.commit('accessToken', null);
// And go to login page
next('/login');
});
},
return next();
},
},
...
],
});
Using Vuex plugins
This is a method I've recently learned.
const storeModerator = (store, router) {
// listen to mutations
store.subscribe(({ type, payload }, state) => {
// if commit('setAccessToken') was called dispatch 'getAuthUser'
if (type === 'setAccessToken') {
store.dispatch('getAuthUser');
}
});
};
export default new Vuex.Store({
...,
plugins: [storeModerator]
});
You can learn more by checking:
Vue-router navigation guards
Vuex Plugins
Decouple Vuex modules with the Mediator pattern
来源:https://stackoverflow.com/questions/51897898/auto-signin-with-vuex-and-vuex-persistedstate