如何访问 beforeEnter 中通过 store 操作异步检索的存储数据?
import store from './vuex/store';
store.dispatch('initApp'); // in here, async data will be fetched and assigned to the store's state
// following is an excerpt of the routes object:
{
path: '/example',
component: Example,
beforeEnter: (to, from, next) =>
{
if (store.state.asyncData) {
// the above state is not available here, since it
// it is resolved asynchronously in the store action
}
}
}
这在第一次页面加载或页面重新加载后尤其重要,此时正在获取初始化数据并且路由器需要等待该数据以允许用户访问该页面或不允许用户访问该页面。
路由器是否可以“等待”数据被获取? 或者结合异步 vuex 存储数据处理导航防护的最佳方法是什么?
(哦,预填充“asyncData”并不是解决方案,因为 beforeEnter 钩子需要根据数据库中的真实数据而不是默认数据做出决定)
您可以通过从 vuex 操作返回一个承诺来做到这一点,正如here所解释的那样,并从
beforeEnter
本身内部调用调度。
代码应如下所示:
import store from './vuex/store';
// following is an excerpt of the routes object:
{
path: '/example',
component: Example,
beforeEnter: (to, from, next) =>
{
store.dispatch('initApp').then(response => {
// the above state is not available here, since it
// it is resolved asynchronously in the store action
}, error => {
// handle error here
})
}
}
您是否真的需要在每次路由更改之前从服务器异步获取数据,或者您是否只需要从存储中保留一些数据,以便它们在页面重新加载或用户使用直接链接时不会“消失” ?
如果是后者,您可以将身份验证数据(例如 JWT 令牌)保存在 localStorage/cookie 中,然后在初始化期间简单地从存储中提取它们(并将它们提交到存储) - 这应该是同步操作。
您还可以使用 vuex-persistedstate 保留商店的整个状态,这样它就不会在重新加载时消失,也不需要水合。
如果您需要在首次加载或页面重新加载之前异步获取一些数据,您可以分派存储操作并在
then()
回调中初始化 Vue 实例 - 但这可能取决于您的实现。像这样的东西(在main.js
中):
import Vue from 'vue';
import VueRouter from 'vue-router';
import { sync } from 'vuex-router-sync';
// contains VueRouter instance and route definitions
import router from 'src/router/router';
// contains Vuex.Store instance. I guess this is './vuex/store' in your example?
import store from 'src/store/store';
// sync the router with vuex store using vuex-router-sync
Vue.use(VueRouter);
sync(store, router);
// dispatch init action, doing asynchronous stuff, commiting to store
store.dispatch('initApp').then(() => {
// create the main Vue instance and mount it
new Vue({
router,
store
}).$mount('#app');
});
我通过使用 store.watch() 解决了这个问题,没有初始值,并在初始化后返回最新值。
这是我的示例代码
async function redirectIfNotAuth (to, from, next) {
const user = await getUserState()
if (user === null) {
next({ name: 'auth' })
} else {
next()
}
}
function getUserState () {
return new Promise((resolve, reject) => {
if (store.state.user === undefined) {
const unwatch = store.watch(
() => store.state.user,
(value) => {
unwatch()
resolve(value)
}
)
} else {
resolve(store.state.user)
}
})
}
/* STORE */
const store = new Vuex.Store({
state: {
user: undefined
}
})
/* ROUTER */
new Router({
routes: [
{
path: '',
name: 'home',
component: Home,
beforeEnter: redirectIfNotAuth
},
{
path: '/signin',
name: 'auth',
component: Auth,
}
]
})
这是我的解决方案:
import {Common_Config} from "@/app/config/Common";
export class Life_Boot {
public static async prepareRemoteLoading(params : any = {}) {
return new Promise(async (resolve, reject) => {
let res = await this.initialize(params)
resolve(res)
})
}
protected static async initialize(params : any = {}) {
const client = Common_Config.init(params)
// load remote config, get user roles and menus, add routes dynamically
await client.context('life').waitInitialized()
return client
}
}
// at the bottom of the main.js
Life_Boot.prepareRemoteLoading().then(client => {
new Vue({
router,
store,
i18n,
render: h => h(App),
}).$mount('#app')
})
最直接的方法是使用await/async,因为你的
initApp
操作也是async
或返回一个承诺
import store from './vuex/store';
// following is an excerpt of the routes object:
{
path: '/example',
component: Example,
async beforeEnter(to, from, next) {
await store.dispatch('initApp');
next();
}
}