pinia 商店中的状态更改后 vue 组件不更新
vue component doesn't update after state changes in pinia store
我目前正在开发我的第一个 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 中用于有条件的 class 渲染。
问题是,当状态更新时,它不会在组件中更新,组件本身也不会更新。我尝试在 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 订阅状态,但没有成功。
storeToRefs()
您需要使用 storeToRefs()
从商店中提取属性,同时保持这些属性的反应性。
import { storeToRefs } from 'pinia'
const themeStore = useThemeStore();
const { isDark } = storeToRefs(themeStore);
从 Pinia 商店获取反应状态的错误方法:
我在下面列出的从 Pinia 商店获取 state(属性、吸气剂)的所有方法都是 错误的:
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); // not reactive
let { isDark } = themeStore; // not reactive, cannot destructure
直接从商店解构actions
。
这里值得一提的是,“您 可以 直接从商店解构操作,因为它们绑定到商店本身。” (docs)
如果您的商店中有一个名为“增量”的操作,您可以直接从组件中的商店中提取它:
...
const { increment } = store // actions can be destructured directly
...
此外,根据Pinia docs,第一个参数是unique ID
,因此您不需要在选项对象中再次指定id
。或者您可以忽略第一个参数并仅将 id
指定为选项。哪种方式都可以。
我目前正在开发我的第一个 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 中用于有条件的 class 渲染。
问题是,当状态更新时,它不会在组件中更新,组件本身也不会更新。我尝试在 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 订阅状态,但没有成功。
storeToRefs()
您需要使用 storeToRefs()
从商店中提取属性,同时保持这些属性的反应性。
import { storeToRefs } from 'pinia'
const themeStore = useThemeStore();
const { isDark } = storeToRefs(themeStore);
从 Pinia 商店获取反应状态的错误方法:
我在下面列出的从 Pinia 商店获取 state(属性、吸气剂)的所有方法都是 错误的:
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); // not reactive
let { isDark } = themeStore; // not reactive, cannot destructure
直接从商店解构actions
。
这里值得一提的是,“您 可以 直接从商店解构操作,因为它们绑定到商店本身。” (docs)
如果您的商店中有一个名为“增量”的操作,您可以直接从组件中的商店中提取它:
...
const { increment } = store // actions can be destructured directly
...
此外,根据Pinia docs,第一个参数是unique ID
,因此您不需要在选项对象中再次指定id
。或者您可以忽略第一个参数并仅将 id
指定为选项。哪种方式都可以。