vue组件在PINIA商店的状态更改后不进行更新

发布于 2025-01-18 05:23:27 字数 5113 浏览 2 评论 0原文

我目前正在开发我的第一个 vue 应用程序,正在构建登录逻辑。 对于状态管理,正在使用 pinia。我创建了一个 Pinia Store 来管理全局的“isLoggedIn”状态。

import { defineStore } from "pinia";

export const useLoginStatusStore = defineStore('loginStatus', {
    id: 'loginStatus',
    state: () => ({
        isLoggedIn: false
    }),
    actions: {
        logIn() {
            this.isLoggedIn = true
            console.log("Login", this.isLoggedIn)
        },
        logOut() {
            this.isLoggedIn = false
            console.log("Logout", this.isLoggedIn)
        }
    }
})

到目前为止一切顺利,它工作正常,我可以访问组件和路由器文件中的状态和操作。

**<roouter.js>**

import { createRouter, createWebHistory } from 'vue-router'
import { createPinia } from 'pinia'
import { createApp, ref } from 'vue'
import { useLoginStatusStore } from '../stores/loginStatus.js'

import App from '../App.vue'
import WelcomeView from '../views/public/WelcomeView.vue'
import SplashView from '../views/public/SplashView.vue'

const pinia = createPinia()
const app = createApp(App)
app.use(pinia)

const loginStatusStore = useLoginStatusStore()
let isLoggedIn = ref(loginStatusStore.isLoggedIn)

console.log("isLoggedIn", loginStatusStore.isLoggedIn)


const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'splash',
      component: SplashView
    },
    {
      path: '/welcome',
      name: 'welcome',
      component: WelcomeView
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/public/LoginView.vue')
    },
    {
      path: '/signup',
      name: 'signup',
      component: () => import('../views/public/SignUpView.vue')
    },
    {
      path: '/resetpassword',
      name: 'resetpassword',
      component: () => import('../views/public/ForgotPasswordView.vue')
    },
    {
      path: '/home',
      name: 'home',
      component: () => import('../views/protected/HomeView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/sounds',
      name: 'sounds',
      component: () => import('../views/protected/SoundsView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/player',
      name: 'soundPlayer',
      component: () => import('../views/protected/SoundPlayerView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/profile',
      name: 'profile',
      component: () => import('../views/protected/ProfileView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/meditation',
      name: 'meditation',
      component: () => import('../views/protected/MeditationView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/tools',
      name: 'tools',
      component: () => import('../views/protected/ToolsView.vue'),
      meta: { requiresAuth: true }
    }
  ]
})

router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth) {
    console.log("Router", isLoggedIn.value)
    if (!isLoggedIn.value) {
      next({
        name: 'welcome'
      })
    } else {
      next()
    }
  } else {
    next()
  }
})

export default router

在路由器中,它用于受保护的路由,在 App.vue 中用于条件类渲染。

问题是,当状态更新时,它不会在组件中更新,组件本身也不会更新。我尝试在 pinia 中使用 $subscribe 方法,但没有成功。我知道,这里需要的是能够产生反应性的东西。但不知道如何做到这一点。我很感谢您对此提供的任何帮助:)

感谢您的阅读

**App.vue**

<script setup>
import { RouterView } from 'vue-router';
import DevNavItem from '@/components/header/DevNavItem.vue'
import HeaderItem from '@/components/header/HeaderItem.vue'
import FooterItem from '@/components/footer/FooterItem.vue'
import { useLoginStatusStore } from './stores/loginStatus.js';

const loginStatusStore = useLoginStatusStore()
const isLoggedIn = loginStatusStore.isLoggedIn

console.log("App.vue", loginStatusStore.isLoggedIn)

</script>

<template>
  <DevNavItem />
  <HeaderItem v-if="isLoggedIn" />
  <RouterView :class="isLoggedIn ? 'mainProtected' : 'mainPublic'" />
  <FooterItem v-if="isLoggedIn" />
</template>

<style>
/*FONT-IMPORT*/
@import url("@/assets/font/alegreya_font.scss");

/* GENERAL STYLES */

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
header {
  position: top;
}
.mainProtected {
  width: 100vw;
  height: 83vh;
  overflow: hidden;
}
.mainPublic {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

/* GLOBAL CLASSES */

.mainLogo {
  height: 350px;
  width: 350px;
  background: url("./img/icons/main.png") center/cover no-repeat;
}
.leavesBackground {
  background-color: #253334;
  background-image: url("./src/img/images/background_partial.png");
  background-repeat: no-repeat;
  background-position: bottom;
  background-size: contain;
}
.logoSmall {
  background: url("./img/icons/main.png") center/contain no-repeat;
  height: 100px;
  width: 100px;
}
.buttonPublic {
  padding: 20px 0;
  text-align: center;
  background-color: #7c9a92;
  color: #fff;
  border-radius: 15px;
  width: 90%;
  text-decoration: none;
  font-size: 24px;
  border: none;
}
</style>

我尝试使用 $subscribe 订阅该状态,但没有成功。

I'm currently working on my first vue application, currently building the login logics.
For State management, pinia is being used. I created a Pinia Store to manage the "isLoggedIn" state globally.

import { defineStore } from "pinia";

export const useLoginStatusStore = defineStore('loginStatus', {
    id: 'loginStatus',
    state: () => ({
        isLoggedIn: false
    }),
    actions: {
        logIn() {
            this.isLoggedIn = true
            console.log("Login", this.isLoggedIn)
        },
        logOut() {
            this.isLoggedIn = false
            console.log("Logout", this.isLoggedIn)
        }
    }
})

So far so good, its working, i can access the state and actions in the components and router file.

**<roouter.js>**

import { createRouter, createWebHistory } from 'vue-router'
import { createPinia } from 'pinia'
import { createApp, ref } from 'vue'
import { useLoginStatusStore } from '../stores/loginStatus.js'

import App from '../App.vue'
import WelcomeView from '../views/public/WelcomeView.vue'
import SplashView from '../views/public/SplashView.vue'

const pinia = createPinia()
const app = createApp(App)
app.use(pinia)

const loginStatusStore = useLoginStatusStore()
let isLoggedIn = ref(loginStatusStore.isLoggedIn)

console.log("isLoggedIn", loginStatusStore.isLoggedIn)


const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'splash',
      component: SplashView
    },
    {
      path: '/welcome',
      name: 'welcome',
      component: WelcomeView
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/public/LoginView.vue')
    },
    {
      path: '/signup',
      name: 'signup',
      component: () => import('../views/public/SignUpView.vue')
    },
    {
      path: '/resetpassword',
      name: 'resetpassword',
      component: () => import('../views/public/ForgotPasswordView.vue')
    },
    {
      path: '/home',
      name: 'home',
      component: () => import('../views/protected/HomeView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/sounds',
      name: 'sounds',
      component: () => import('../views/protected/SoundsView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/player',
      name: 'soundPlayer',
      component: () => import('../views/protected/SoundPlayerView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/profile',
      name: 'profile',
      component: () => import('../views/protected/ProfileView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/meditation',
      name: 'meditation',
      component: () => import('../views/protected/MeditationView.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/tools',
      name: 'tools',
      component: () => import('../views/protected/ToolsView.vue'),
      meta: { requiresAuth: true }
    }
  ]
})

router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth) {
    console.log("Router", isLoggedIn.value)
    if (!isLoggedIn.value) {
      next({
        name: 'welcome'
      })
    } else {
      next()
    }
  } else {
    next()
  }
})

export default router

In the router it's being used for protected routes and in App.vue for conditional class rendering.

The Problem is, that when the state gets updated, it doesn't get updated in the components and the components themselves don't update either. I tried with the $subscribe method in pinia, but didnt manage to get it working. I know, whats needed is something that creates reactivity here. But no clue how to do that. I'm grateful for any help with this :)

thanks for reading

**App.vue**

<script setup>
import { RouterView } from 'vue-router';
import DevNavItem from '@/components/header/DevNavItem.vue'
import HeaderItem from '@/components/header/HeaderItem.vue'
import FooterItem from '@/components/footer/FooterItem.vue'
import { useLoginStatusStore } from './stores/loginStatus.js';

const loginStatusStore = useLoginStatusStore()
const isLoggedIn = loginStatusStore.isLoggedIn

console.log("App.vue", loginStatusStore.isLoggedIn)

</script>

<template>
  <DevNavItem />
  <HeaderItem v-if="isLoggedIn" />
  <RouterView :class="isLoggedIn ? 'mainProtected' : 'mainPublic'" />
  <FooterItem v-if="isLoggedIn" />
</template>

<style>
/*FONT-IMPORT*/
@import url("@/assets/font/alegreya_font.scss");

/* GENERAL STYLES */

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
header {
  position: top;
}
.mainProtected {
  width: 100vw;
  height: 83vh;
  overflow: hidden;
}
.mainPublic {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

/* GLOBAL CLASSES */

.mainLogo {
  height: 350px;
  width: 350px;
  background: url("./img/icons/main.png") center/cover no-repeat;
}
.leavesBackground {
  background-color: #253334;
  background-image: url("./src/img/images/background_partial.png");
  background-repeat: no-repeat;
  background-position: bottom;
  background-size: contain;
}
.logoSmall {
  background: url("./img/icons/main.png") center/contain no-repeat;
  height: 100px;
  width: 100px;
}
.buttonPublic {
  padding: 20px 0;
  text-align: center;
  background-color: #7c9a92;
  color: #fff;
  border-radius: 15px;
  width: 90%;
  text-decoration: none;
  font-size: 24px;
  border: none;
}
</style>

I tried subscribing to the state with $subscribe, but it didn't work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冷…雨湿花 2025-01-25 05:23:27

StoreTorefs()

您需要使用 Storetorefs() 从商店中提取属性,同时使这些属性具有反应性。

import { storeToRefs } from 'pinia'
const themeStore = useThemeStore();
const { isDark } = storeToRefs(themeStore);

@fennec提出了计算的获得反应状态的方式 尽管我不建议使用此方法,因为有一个专用storeTorefs()可用。

import { computed } from 'vue'
const themeStore = useThemeStore();
const isDark = computed(() => themeStore.isDark);

从Pinia商店获得反应性状态的错误方法:

以下所有方法 state (属性,getters)从Pinia商店获得错误

import { useThemeStore } from "./stores/theme.js";
const themeStore = useThemeStore();

// WRONG ways of extracting state from store
let isDark = themeStore.isDark; // not reactive
let isDark = ref(themeStore.isDark); // reactive, but will not be updated with the store
let { isDark } = themeStore; // not reactive, cannot destructure

破坏操作直接从商店中。

值得注意的是,“您可以直接从商店绑定到商店本身时。”

如果您在商店中有一个名为“增量”的操作,则可以直接从组件中的商店中提取它:

...
const { increment } = store // actions can be destructured directly
...

另外,根据“ noreferrer”> pinia docs ,第一个论点是唯一ID,因此您无需在选项对象中再次指定id。或者,您可以忽略第一个参数,然后将id作为选项指定。无论哪种方式都很好。

storeToRefs()

You need to use storeToRefs() to extract properties from the store while keeping those properties reactive.

import { storeToRefs } from 'pinia'
const themeStore = useThemeStore();
const { isDark } = storeToRefs(themeStore);

Computed property

Thanks to @Fennec for suggesting the computed way of getting reactive state. Although I don't recommend this method since there is a dedicated storeToRefs() available.

import { computed } from 'vue'
const themeStore = useThemeStore();
const isDark = computed(() => themeStore.isDark);

WRONG ways to get reactive state from the Pinia store:

All the ways listed below of getting the state (properties, getters) from the Pinia store are WRONG:

import { useThemeStore } from "./stores/theme.js";
const themeStore = useThemeStore();

// WRONG ways of extracting state from store
let isDark = themeStore.isDark; // not reactive
let isDark = ref(themeStore.isDark); // reactive, but will not be updated with the store
let { isDark } = themeStore; // not reactive, cannot destructure

Destructuring actions directly from the store.

Its worth to note here that "you can destructure actions directly from the store as they are bound to the store itself." (docs)

If you have an action named "increment" in your store, you can just extract it directly from the store in your component:

...
const { increment } = store // actions can be destructured directly
...

Also, according to Pinia docs, the first argument is the unique ID, so you do not need to specify the id again inside the options object. Or you can just ignore the first argument and just specify the id as an option. Either way is fine.

岛歌少女 2025-01-25 05:23:27

一种方法是在 store 中创建一个返回所需状态的方法。

function getDesiredState() {
 return desiredState;
}

它是反应式的,无需使用 storeToRefs。

one way is to create a method in store that returns the desired state.

function getDesiredState() {
 return desiredState;
}

it is reactive without using storeToRefs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文