vue组件在PINIA商店的状态更改后不进行更新
我目前正在开发我的第一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
StoreTorefs()
您需要使用
Storetorefs()
从商店中提取属性,同时使这些属性具有反应性。。
@fennec提出了
计算的
获得反应状态的方式 尽管我不建议使用此方法,因为有一个专用storeTorefs()
可用。从Pinia商店获得反应性状态的错误方法:
以下所有方法 state (属性,getters)从Pinia商店获得错误:
破坏
操作
直接从商店中。值得注意的是,“您可以直接从商店绑定到商店本身时。” (
如果您在商店中有一个名为“增量”的操作,则可以直接从组件中的商店中提取它:
另外,根据“ noreferrer”> pinia docs ,第一个论点是
唯一ID
,因此您无需在选项对象中再次指定id
。或者,您可以忽略第一个参数,然后将id
作为选项指定。无论哪种方式都很好。storeToRefs()
You need to use
storeToRefs()
to extract properties from the store while keeping those properties reactive.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 dedicatedstoreToRefs()
available.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:
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:
Also, according to Pinia docs, the first argument is the
unique ID
, so you do not need to specify theid
again inside the options object. Or you can just ignore the first argument and just specify theid
as an option. Either way is fine.一种方法是在 store 中创建一个返回所需状态的方法。
它是反应式的,无需使用 storeToRefs。
one way is to create a method in store that returns the desired state.
it is reactive without using storeToRefs.