Vue: не удается получить доступ к магазину Pinia до входа в vue-router

Я использую Vue 3, включая Composition API, а также Pinia в качестве управления состоянием.

В API опций есть метод beforeRouteEnter, встроенный в сам компонент. К сожалению, этот метод не существует в API композиции. Здесь код, который был бы в методе beforeRouteEnter, записывается непосредственно в метод setup. Однако это означает, что сначала загружается и отображается компонент, затем выполняется код и, если проверка не проходит, компонент перенаправляется, например, на страницу с ошибкой.

Моя идея заключалась в том, чтобы сделать мою проверку непосредственно в конфигурации маршрута в методе маршрута beforeEnter. Однако у меня нет доступа к магазину Pinia, который, похоже, еще не инициализирован, хотя он вызывается ранее в main.js.

Журнал консоли

      Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
    const pinia = createPinia()
    app.use(pinia)
This will fail in production.

Router.js

      import { useProcessStore } from "@/store/process";

const routes: Array<RouteRecordRaw> = [
{
    path: "/processes/:id",
    name: "ProcessView",
    component: loadView("ProcessView", "processes/"),
    beforeEnter: () => {
      const processStore = useProcessStore();
      console.log(processStore);
    },
    children: [
      {
        path: "steer",
        name: "ProcessSteer",
        component: loadView("ProcessSteer", "processes/")
      },
      {
        path: "approve/:code",
        name: "ProcessApprove",
        component: loadView("ProcessApprove", "processes/")
      }
    ]
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;

main.js

      import { createApp } from "vue";
import "@/assets/bundle-bootstrap.css";
import App from "@/App.vue";
import { createPinia } from "pinia";
import router from "@/router";
import SvgIcon from "@/components/SvgIcon.vue";

const pinia = createPinia();
const app = createApp(App);

app.use(pinia);
app.use(router);
app.component("SvgIcon", SvgIcon);

router.isReady().then(() => {
  app.mount("#app");
});

5 ответов

Однако у меня нет доступа к магазину Pinia, который, похоже, еще не инициализирован, хотя он вызывается ранее в main.js.

Перед чем? Экземпляр Pinia создается с помощью const pinia = createPinia(); после _ routerмодуль импортирован - пока он импортируется, все побочные эффекты, включая вызов createRouter()казнены. Как только маршрутизатор создан, он начинает свою начальную навигацию (на клиенте - на сервере вам нужно запустить его с помощью router.push()) - если вы оказались по URL-адресу, совпадающему с маршрутом с охранником, использующим магазин Pinia, useProcessStore()происходит до создания Пинии...

Использование хранилища вне компонента

У вас есть два варианта:

  • либо вы убедитесь, что любой вызов происходит после создания Pinia ( createPinia()) и установил( app.use(pinia))
  • или вы передаете экземпляр Pinia в любой useXXXStore()вне компонента...
      // store.js
import { createPinia } from "pinia";

const pinia = createPinia();

export default pinia;
      // router.js
import pinia from "@/store.js";
import { useProcessStore } from "@/store/process";

const routes: Array<RouteRecordRaw> = [
{
    path: "/processes/:id",
    name: "ProcessView",
    component: loadView("ProcessView", "processes/"),
    beforeEnter: () => {
      const processStore = useProcessStore(pinia ); // <-- passing Pinia instance directly
      console.log(processStore);
    },
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;
      // main.js
import { createApp } from "vue";
import App from "@/App.vue";
import store from "@/store.js";
import router from "@/router";

const app = createApp(App);

app.use(store);
app.use(router);

router.isReady().then(() => {
  app.mount("#app");
});

Надеюсь, это будет полезно. Vue обеспечивает поддержку некоторых функций, в которых нам нужно хранить (вне компонентов).

Чтобы решить эту проблему, я просто позвонил вuseStore()внутри функции, предоставляемой Vue(beforeEach), и это сработало.

Ссылка: https://pinia.vuejs.org/core-concepts/outside-component-usage.html

Пример :

      import { useAuthStore } from "@/stores/auth";
.
.
.
.
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

router.beforeEach(async (to, from) => {
  const authStore = useAuthStore();

  // use authStore Here
});

У меня такая же проблема с доступом к хранилищу в методе «beforeEach» для управления авторизацией.

Я использую этот метод в main.js, а не в router.js. в магазине router.js недоступен.

создать экземпляр pinia в piniCreate.js

      //piniaCreate.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;

после этого создайте мой магазин в mainStore.js

      import { defineStore } from 'pinia'
export const mainStore = defineStore('counter', {
    state: () => {
        return {
            user: {
                isAuthenticated: isAuthen,
            }
        }
    },
    actions: {
        login(result) {
            //...
            this.user.isAuthenticated = true;
        }  , 
        logOff() {
            this.user.isAuthenticated = false;
        }
    }
});

Затем я использовал метод beforeEach в файле main.js.

      //main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from "@/stores/piniaCreate";
import { mainStore } from '@/stores/mainStore';

import router from './router'

const app = createApp(App)
    .use(pinia)
    .use(router)
const store1 = mainStore();

router.beforeEach((from) => {
    if (from.meta.requiresAuth && !store1.user.isAuthenticated) {
        router.push({ name: 'login', query: { redirect: from.path } });
    }
})
app.mount('#app');

Вы можете передать метод во втором параметре definestore:

store.js

      export const useAppStore = defineStore('app', () => {
  const state = reactive({
    appName: 'App',
    appLogo: ''
  })

  return {
    ...toRefs(state)
  }
})

router.js

      router.beforeEach((to, from, next) => {
  const apppStore = useAppStore()
  next()
})

Я решил это, добавив ленивую загрузку

      const routes = [
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]
Другие вопросы по тегам