当前位置: 首页 > news >正文

VueX 与Pinia 一篇搞懂

VueX 简介

Vue官方:状态管理工具

状态管理是什么

需要在多个组件中共享的状态、且是响应式的、一个变,全都改变。

例如一些全局要用的的状态信息:用户登录状态、用户名称、地理位置信息、购物车中商品、等等

这时候我们就需要这么一个工具来进行全局的状态管理,Vuex就是这样的一个工具。

单页面的状态管理

View–>Actions—>State

视图层(view)触发操作(action)更改状态(state)响应回视图层(view)

多页状态管理

vuex store对象属性介绍

vue3 中的 setup 在 beforecreate 和 created 前执行,此时 vue对象还未被创建,没有了之前的this,所以此处我们需要用到另一种方法来获取到 store 对象。

import { useStore } from 'vuex' // 引入useStore 方法
const store = useStore()  // 该方法用于返回store 实例
console.log(store)  // store 实例对象

1. state

存放数据的地方

state: {
  count: 100,
  num: 10
},

可以在 state 中直接进行数据变化的操作,但Vue不建议这么做。因为对于vue开发工具 devtools 来说,直接在state中进行数据改变,devtools是跟踪不到的。vuex中希望通过 action(进行异步操作)或是 mutations(同步操作)来进行数据改变的操作,这样在 devtools 中才能直接观察出数据的变化以及记录,方便开发者调试。

另外,在vue3 中对state 中对象新增属性或删除时,不再需要通过 vue.set() , 或是 vue.delete() 来进行对象的响应式处理了,直接新增的对象属性已经具有响应式

2. mutations

vuex的store状态更新的唯一方式:提交 mutation

同步操作可以直接在mutatuions中直接进行

mutions 主要包含2部分:

  1. 字符串的事件类型 (type)

  2. 一个**回调函数(handler)**该回调函数的第一个参数是 state

    mutations: {
    // 传入 state
    increment (state) {
    state.count++
    }
    }

template 中通过 $store.commit('方法名') 触发

// 导入 useStore 函数
import { useStore } from 'vuex'
const store = useStore()
store.commit('increment')

mution 的参数与传参方法

mution 接收参数直接写在定义的方法里边即可接受传递的参数

// ...state定义count
mutations: {sum (state, num) {state.count += num}
}

通过 commit 的payload 进行参数传递

使用 store.commit('mution中函数名', '需要传递的参数' ) 在commit里添加参数的方式进行传递

<h2>{{this.$store.state.count}}</h2>
<button @click="add(10)">++</button>
...
<script setup>
// 获取store实例,获取方式看上边获取store实例方法
const add = (num) => {store.commit('sum', num)
}
</script>

mution 的提交风格

前面提到了 mution 主要包含 type 和 回调函数 两部分, 和通过commit payload的方式进行参数传递(提交),下面我们可以

用这种方式进行 mution 的提交

const add = (num) => {store.commit({type: 'sum',  // 类型就是mution中定义的方法名称num})
}...
mutations: {sum (state, payload) {state.count += payload.num}
}

 3. actions

异步操作在action中进行,再传递到mutation

action基本使用如下:

action 中定义的方法默认参数为** context 上下文**, 可以理解为 store 对象

通过 context 上下文对象,拿到store,通过 commit 触发 mution 中的方法,以此来完成异步操作

...
mutations: {sum (state, num) {state.count += num}
},
actions: {// context 上下文对象,可以理解为storesum_actions (context, num) {setTimeout(() => {context.commit('sum', num)  // 通过context去触发mutions中的sum}, 1000)}
},

在template 中通过dispatch 调用action 中定义的sum_action 方法

// ...template
store.dispatch('sum_actions', num)

通过 promise 实现异步操作完成,通知组件异步执行成功或是失败。

// ...
const addAction = (num) => {store.dispatch('sum_actions', {num}).then((res) => {console.log(res)}).catch((err) => {console.log(err)})
}

sun_action方法返回一个promise,当累加的值大于30时不再累加,抛出错误。

 actions: {sum_actions (context, payload) {return new Promise((resolve, reject) => {setTimeout(() => {// 通过 context 上下文对象拿到 countif (context.state.count < 30) {context.commit('sum', payload.num)resolve('异步操作执行成功')} else {reject(new Error('异步操作执行错误'))}}, 1000)})}},

4. getters

类似于组件的计算属性

import { createStore } from 'vuex'export default createStore({state: {students: [{ name: 'mjy', age: '18'}, { name: 'cjy', age: '22'}, { name: 'ajy', age: '21'}]},getters: {more20stu (state) { return state.students.filter(item => item.age >= 20)}}
})

使用 通过$store.getters.方法名 进行调用

//...template
<h2>{{$store.getters.more20stu}}</h2> // 展示出小于20岁的学生

getters 的入参, getters 可以接收两个参数,一个是 state, 一个是自身的 getters ,并对自身存在的方法进行调用。

getters: {more20stu (state, getters) { return getters.more20stu.length}
}

getters 的参数与传参方法

上面是getters固定的两个参数,如果你想给getters传递参数,让其筛选大于 age 的人,可以这么做

返回一个 function 该 function 接受 Age,并处理

getters: {more20stu (state, getters) { return getters.more20stu.length},moreAgestu (state) {return function (Age) {return state.students.filter(item =>item.age >= Age)}}// 该写法与上边写法相同但更简洁,用到了ES6中的箭头函数,如想了解es6箭头函数的写法// 可以看这篇文章 https://blog.csdn.net/qq_45934504/article/details/123405813?spm=1001.2014.3001.5501moreAgestu_Es6: state => {return Age => {return state.students.filter(item => item.age >= Age)}}
}

使用

//...template
<h2>{{$store.getters.more20stu}}</h2> // 展示出小于20岁的学生
<h2>{{$store.getters.moreAgestu(18)}}</h2> // 通过参数传递, 展示出年龄小于18的学生

5. modules

当应用变得复杂时,state中管理的变量变多,store对象就有可能变得相当臃肿。

为了解决这个问题,vuex允许我们将store分割成模块化(modules),而每个模块拥有着自己的state、mutation、action、getters等

在store文件中新建modules文件夹

在modules中可以创建单一的模块,一个模块处理一个模块的功能

store/modules/user.js 处理用户相关功能

store/modules/pay.js 处理支付相关功能

store/modules/cat.js 处理购物车相关功能

// user.js模块
// 导出
export default {namespaced: true, // 为每个模块添加一个前缀名,保证模块命明不冲突 state: () => {},mutations: {},actions: {}
}

最终通过 store/index.js 中进行引入

// store/index.js
import { createStore } from 'vuex'
import user from './modules/user.js'
import user from './modules/pay.js'
import user from './modules/cat.js'
export default createStore({modules: {user,pay,cat}
})

在template中模块中的写法和无模块的写法大同小异,带上模块的名称即可

<h2>{{$store.state.user.count}}</h2>store.commit('user/sum', num) // 参数带上模块名称
store.dispatch('user/sum_actions', sum)


 

一、使用vuex

vuex的安装:

npm i vuex

vuex的配置,@/store/index.js:

import {createStore} from 'vuex'//导入createStore构造函数
export default createStore({ state:{ //Vuex的状态,实际上就是存数据的地方person:{name:'jack',age:200}},getters:{ //提供获取Vux状态的方式, 注意在组件中调用时getPerson是以属性的方式被访问getPerson(state){return state.person}},mutations:{ //提供直接操作Vuex的方法,注意mutations里的方法中不能有任何异步操做ageGrow(state, value){//第一个参数state为Vuex状态;第二个参数为commit函数传来的值state.person.age += value}},actions:{ //提供通过mutations方法来简介操作Vuex的方法ageGrow(context, value){ //第一个参数context为上下文,提供一些方法;第二个参数为dispatch函数传来的值context.commit('ageGrow', value)}}, 
})

在@/main.js中引入:

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')

在组件中使用Vuex:

<template><h1>名字:{{person.name}}--年龄:{{person.age}}</h1><input type="text" v-model="value"><button @click="ageGrow(value)">增加年龄</button><!-- 在input框输入一个数字,点击按钮,可以看到年龄发生变化,说明Vuex正常工作 -->
</template><script>import {useStore} from 'vuex' import {ref} from 'vue'export default {setup(){const store = useStore()    //获取store对象let person = store.getters.getPerson    //从组件中获取状态(数据)person 方式一// let person = store.state.person      //从组件中获取状态(数据)person 方式二let value = ref('输入年龄的增量')function ageGrow(ageGrowth){ageGrowth = parseInt(ageGrowth)if(isNaN(ageGrowth)){ageGrowth = 0}store.dispatch('ageGrow', ageGrowth)//通过dispatch来调用actions里的'ageGrow'方法,参数为ageGrowth//actions的方法又会通过commit来调用mutations里的方法,从而引起状态(数据)的变化//也可以在组件里跳过dispatch actions,直接store.commit}return {person, value,ageGrow}}}
</script>
<style></style>

小结:安装完vuex之后,首先要用creatRouter构造函数创建一个router对象,并在main.js中引入这个对象。然后在组件中,通过userStore方法来获取这个router对象,进一步通过getter或者state可以得到Vuex状态(数据),通过dispatch->actions->mutations->state的数据传送方式可以操作和改变Vuex的状态(数据)

2. Module

Vuex官方原话:“由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。”

什么叫单一状态树呢,其实就是上文中的state对象。在Vuex的基本使用中,我们使用state对象来存储Vuex的状态,state对象里面可以嵌套其他的对象,它是一个树形的结构,而且这个state对象是唯一的,所有的状态都要存储在这一个对象里。因此,我们称之为单一状态树。
这种单一状态树的弊端是显而易见的,对于中大型项目来说,要托管给Vuex的状态有很多,把这些海量的数据如果都塞到一个文件里面的一个对象里面,未免显得过于臃肿,不管是开发起来还是维护起来都会有很多不变。
对此,官方给出了解决方案:
 

“为了解决以上问题,Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割”

2.1 vuex中模块化的基本使用

vuex的模块化没什么难理解的,就是把store给拆开,一个对象拆成多个对象,一个文件拆成多个文件,并且彼此可以拥有独立的命名空间。
道理很简单,所以直接看样例:

文件结构

├──src├── components│   └── Test.vue└── store├── index.js└── modules├── male.js└── female.js

index.js

import {createStore} from 'vuex'
import female from './modules/female'   //导入模块
import male from './modules/male'       //导入模块
export default createStore({ modules:{   //使用模块female,male}
})

male.js

export default {namespaced:true,    //namespaced:true代表该模块带有独立命名空间state:{             //否则,默认是处于全局命名空间,就和非模块化一样personList:[{name:'张飞', id:'004'},{name:'武则天', id:'005'},{name:'秀吉', id:'006'},]},mutations:{addMutation(state, value){      //往personList中添加一个人state.personList.push(value)},removeMutaion(state, value){    //往personList中删除一个人state.personList = state.personList.filter((el) => el.id != value.id)}},actions:{addAction(context, value){setTimeout(() => {context.commit('addMutation', value) // ->'male/addMutation'}, 1000);},removeAction(context, value){context.commit('removeMutaion', value)}},getters:{personList(state){return state.personList}}
}

female.js

export default {namespaced:true,    //namespaced:true代表该模块带有独立命名空间state:{             //否则,默认是处于全局命名空间,就和非模块化一样personList:[{name:'李白', id:'001'},{name:'孙尚香', id:'002'},{name:'大乔', id:'003'},]},mutations:{addMutation(state, value){      //往personList中添加一个人state.personList.push(value)},removeMutaion(state, value){    //往personList中删除一个人state.personList = state.personList.filter((el) => el.id != value.id)}},actions:{addAction(context, value){setTimeout(() => {context.commit('addMutation', value) // ->'female/addMutation'}, 1000);},removeAction(context, value){context.commit('removeMutaion', value)}},getters:{personList(state){return state.personList}}
}

Test.vue

<template><h1>女人:</h1><li v-for="femalePerson in femalePersons" :key="femalePerson.id">{{femalePerson.name}}<button @click="addToMale(femalePerson)">添加到男人</button></li><h1>男人:</h1><li v-for="malePerson in malePersons" :key="malePerson.id">{{malePerson.name}}<button @click="addToFemale(malePerson)">添加到女人</button></li><!-- 有两个列表,分布是男人和女人,通过点击按钮可以把列表中的某些项添加到另一个列表中 --><!-- 建议粘贴复制并运行代码,这样更直观 -->
</template><script>import { computed } from '@vue/runtime-core';import {useStore} from 'vuex'export default {setup(){let store = useStore()let malePersons = computed(() => store.getters['male/personList']) //通过getter获取statelet femalePersons = computed(() => store.state.female.personList)   //直接获取statefunction addToMale(person){store.dispatch('male/addAction', person)store.dispatch('female/removeAction', person)//如果模块中namespaced === true,那么要在方法名前面添加模块的逻辑路径//index.js里使用的模块为路径的起点。//比如index里面有一个moduleA,moduleA有一个子模块moduleB,module有一个action是actionx//那么调用方式为 store.dispatch('moduleA/moduleB/actionx', value)}function addToFemale(person){store.dispatch('female/addAction', person)store.dispatch('male/removeAction', person)}return {malePersons,femalePersons,addToMale,addToFemale}}}
</script>
<style></style>

2.2 在命名空间中访问全局内容

什么是全局内容?不在同一个模块中的内容就是全局内容。
比如,对于上文中的female模块来说,male模块中的getters state action mutations就是全局内容,接下来将会讲解如何在一个模块中访问到全局内容。
为了便于理解,我创造了一个新的样例:

├──src├── components│   └── Test.vue└── store├── index.js└── modules├── moduleA.js└── moduleB.js

index是所有的模块的根,moduleA和moduleB是两个子模块,接下来要做的事情就是在index.js、moduleA.js、moduleB.js中写一些getters state action mutations,最终达成的效果是在index中访问moduleA的内容,在moduleA中访问moduleB的内容,在moduleB中访问index的内容。
Test.vue:

<template><li> {{rootModule.name}}---{{rootModule.num}} <button @click="rootClick">rootAction---A++</button> </li><li> {{moduleA.name}}---{{moduleA.num}} <button @click="aClick">moduleAction---B++</button> </li><li> {{moduleB.name}}---{{moduleB.num}} <button @click="bClick">moduleAction---root++</button> </li><!-- 点击root,moduleA数字加一;点击moduleA,moduleB数字加一;点击moduleB,root数字加一 --><button @click="store.dispatch('globalAction')">触发全局action</button>
</template><script>import {useStore} from 'vuex'export default {setup(){let store = useStore()console.log(store.getters);let rootModule = store.getters['moduleB/rootStateThroughModuleB'].info //通过moduleB的getters获得root的状态let moduleA = store.getters['moduleAStateThroughRoot'].info            //通过root的getters获得moduleA的状态let moduleB = store.getters['moduleA/moduleBStateThroughModuleA'].info //通过moduleA的getters获得moduleB的状态// let moduleB = store.state.moduleB.info// let moduleA = store.state.moduleA.info// let rootModule = store.state.infofunction rootClick(){store.dispatch('addAction', 1)}    //调用root中的action,改变的是moduleA的状态function aClick(){store.dispatch('moduleA/addAction', 1)}//调用moduleA中的action,改变的是moduleB的状态function bClick(){store.dispatch('moduleB/addAction', 1)}//调用moduleB中的action,改变的是root的状态return {rootModule,moduleA,moduleB,rootClick,aClick,bClick,store}}}
</script>
<style></style>

3. vuex的typescript用法

vuex的typescript用法其实就是把state加上ts里的类型限制

3.1 不使用模块化

store文件

// store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'// 为 store state 声明类型
export interface State {count: number
}// 定义 injection key
export const key: InjectionKey<Store<State>> = Symbol()export const store = createStore<State>({state: {count: 0}
})

在main.ts里引入

// main.ts
import { createApp } from 'vue'
import { store, key } from './store'const app = createApp({ ... })// 传入 injection key
app.use(store, key)app.mount('#app')

在组件中使用useStore()获取store,将上述 injection key 传入 useStore 方法可以获取类型化的 store。

// vue 组件
import { useStore } from 'vuex'
import { key } from './store'export default {setup () {const store = useStore(key)store.state.count // 类型为 number}
}

在每个组件中都导入key有些重复且麻烦,我们可以将useStore()封装成myUseStore()

// 在store.ts增加下面几行
import { useStore } from 'vuex'
export function myUseStore () {return useStore(key)
}

3.2 使用模块化

store/index.ts

import { createStore, Store  } from 'vuex'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'
import { InjectionKey } from 'vue'//模块A state的类型
interface moduleAState {name:string,age:number
}//模块B state的类型
interface moduleBState {id:string,adult:boolean
}//vuex state类型
interface State{moduleA:moduleAState,moduleB:moduleBState 
}//如果使用模块化的话,在createStore参数里面就不要写state了,否则会报错
export const key: InjectionKey<Store<State>> = Symbol()
export const store =  createStore<State>({// state:{}, //不可以加这一行,否则报错modules: {moduleA,moduleB}
})

在组件中访问:

// vue 组件
import { useStore } from 'vuex'
import { key } from './store'export default {setup () {const store = useStore(key)store.state.moduleA// 类型为 moduleAStatestore.state.moduleB// 类型为 moduleBState}
}

Pinia 简介 

Pinia是vue生态里Vuex的替代者,一个全新的vue状态管理库。在Vue3成为正式版以后,尤雨溪强势推荐的项目就是Pinia。
那先来看看Pinia比Vuex好的地方,也就是Pinia的五大优势。

可以对Vue2和Vue3做到很好的支持,也就是老项目也可以使用Pinia。
抛弃了Mutations的操作,只有state、getters和actions.极大的简化了状态管理库的使用,让代码编写更加容易直观。
不需要嵌套模块,符合Vue3的Composition api ,让代码更加扁平化。
完整的TypeScript支持。Vue3版本的一大优势就是对TypeScript的支持,所以Pinia也做到了完整的支持。如果你对Vuex很熟悉的化,一定知道Vuex对TS的语法支持不是完整的(经常被吐槽)。
代码更加简洁,可以实现很好的代码自动分割。Vue2的时代,写代码需要来回翻滚屏幕屏幕找变量,非常的麻烦,Vue3的Composition api完美了解决这个问题。 可以实现代码自动分割,pinia也同样继承了这个优点。

安装和配置Pinia

安装和配置Pinia非常简单,像其他Vue插件一样,Pinia需要通过yarn或npm进行安装并且与Vue应用程序进行绑定,可以使用以下命令进行安装:

yarn add pinia
# 或者使用 npm
npm install pinia
 

在安装完Pinia包之后,需要在main.ts文件中导入createPinia函数并将Pinia插件与Vue应用程序绑定,如下所示:

import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';const app = createApp(App);const pinia = createPinia();
app.use(pinia);app.mount('#app');

使用 createPinia() 函数创建并初始化Pinia插件实例,将其与Vue应用程序绑定使用app.use(pinia)。至此,我们就可以使用Pinia来管理Vue应用程序的状态了。

Pinia的核心

Store

Store是 Pinia 中管理状态的核心概念。它相当于一个 Vue 组件中的状态,但是 Store是一个独立的模块。

Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字,这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use… 是一个符合组合式函数风格的约定。

defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。
定义Store的示例代码:

import { defineStore } from 'pinia'// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,
同时以 `use` 开头且以 `Store` 结尾。
(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {// 其他配置...
})
  • defineStore( ) 方法的第一个参数:相当于为容器起一个名字。注意:这里的名字必须唯一,不能重复。
  • defineStore( ) 方法的第二个参数:可以简单理解为一个配置对象,里边是对容器仓库的配置说明。当然这种说明是以对象的形式。
  • state 属性: 用来存储全局的状态的,这里边定义的,就可以是为SPA里全局的状态了。
  • getters属性: 用来监视或者说是计算状态的变化的,有缓存的功能。
  • actions属性: 对state里数据变化的业务逻辑,需求不同,编写逻辑不同。说白了就是修改state全局状态数据的。
     

State

State 是 store 中存储数据的地方。通过定义 State,可以在 store 的任何位置访问和修改数据。

在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。
定义State的示例代码如下:

import { defineStore } from 'pinia'const useStore = defineStore('storeId', {// 为了完整类型推理,推荐使用箭头函数state: () => {return {// 所有这些属性都将自动推断出它们的类型count: 0,name: 'Eduardo',isAdmin: true,items: [],hasChanged: true,}},
})

Getter

Getter 用来获取从 state 派生的数据,类似于 Vue 组件中的 computed 计算属性。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数:

export const useStore = defineStore('main', {state: () => ({count: 0,}),getters: {doubleCount: (state) => state.count * 2,},
})

Action


Action 相当于组件中的 方法。它们可以通过 defineStore() 中的 actions 属性来定义;Action 是一种将异步操作封装在 store中的方式,它是一个可以被调用的函数,也可以接收参数并修改 store 中的状态。 Action应该始终是同步的,并返回一个 Promise 对象,以便在处理异步操作时能够很好地处理结果。

Pinia 中的 Action 由 defineStore 创建,可以通过在 actions 中定义它们来使用它们。例如,下面是一个 store 中的 Action 定义:

import { defineStore } from 'pinia'export const myStore = defineStore('myStore',{ state: () => ({message: 'Hello',}),actions: {async fetchMessage() {const response = await fetch('http://127.0.0.1:5173/message')const data = await response.json()this.message = data.message},},
})

在上面的示例中,我们为 myStore 定义了一个 Action , fetchMessage() ,它会从后台 API 中获取数据,并更新 store 中的状态。然后,我们可以从组件或其他 Action 中调用该 Action :

import { useStore } from 'pinia'export default {setup() {const store = useStore('myStore')function handleClick() {store.fetchMessage()}return {handleClick,}},
}

创建和使用Pinia

创建Pinia

前面我们已经安装和配置好了Pinia,在创建Pinia之前,为了代码的统一管理和可维护性,我们依然先创建一个store文件夹,然后在来创建相关的Pinia,具体步骤如下

  1. 在src文件夹下新建store文件夹,后面所有涉及需要Pinia进行状态管理的代码都放在该文件夹下
  2. 在store文件夹下新建movieListStore.js文件,创建完成后,打开该文件
  3. 在movieListStore.js文件中引入Pinia中的defineStore 方法
     
import { defineStore } from 'pinia'

创建defineStore 对象,定义一个useMovieListStore用于接收defineStore创建的对象,并将其通过export default 导出

 const useMovieListStore = defineStore('movie',{ state: () => ({isShow: true,movies: [],}),getters: {getIsShow() {return this.isShow},getMovies() {return this.movies},},actions: {setIsShow(value) {this.isShow = value},async fetchMovies() {const response = await fetch('https://api.movies.com/movies')const data = await response.json()this.movies = data},},
})
export default useMovieListStore 

注意:
这里需要注意,官方建议我们在定义钩子函数时,建议使用use开头Store结尾的命名方式来对上面创建的对象进行命名,如上面的useMovieListStore

使用Pinia

前面我们已经创建好了Pinia,接下来,我们就可以在组件中使用了。
在Vue组件中使用store,我们需要通过 useStore() 函数访问store的实例。
在Vue组件中使用Pinia的步骤如下

1 先使用 import 引入Pinia 中的 useStore

import { useStore } from 'pinia'

2  创建useStore对象

const store = useStore('movie')
  1. 在需要获取状态的地方通过上面定义的store.getIsShow()获取状态
return {isShow: store.getIsShow(),
}

Menu.vue中完整的示例代码如下:

<template><nav><ul><li v-show="isShow">{{ $route.name }} </li><li><router-link to="/">Home</router-link></li><li><router-link to="/movies">Movies</router-link></li></ul></nav>
</template><script>
import { defineComponent } from 'vue'
import { useStore } from 'pinia'export default defineComponent({name: 'Menu',setup() {const store = useStore('movie')return {isShow: store.getIsShow(),}},
})
</script>

Pinia的Setup Store方式定义 Store
Setup Store与Option Store稍有不同,它与 Vue 组合式 API 的 setup 函数 相似,我们通过传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。示例代码如下:

export const useCounterStore = defineStore('counter', () => {const count = ref(0)function increment() {count.value++}return { count, increment }
})

在 Setup Store 中:

  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions

pinia在API里的使用

 1.$reset :重置到初始值

这个 $reset 可以将 state 的数据初始到初始值,比如我们有一个数据,点击按钮改变了,然后我们可以通过这个 API ,将数据恢复到初始状态值。

  2.$subscribe:监听 state 数据变化

$subscribe 使用来监听的,监听 state 数据的变化,只要 state 里面的数据发生了变化,就会自动走这个函数。

   3.$onAction:一调用 actions 就触发

这个看名字就很好理解了吧,就是 action 一调用就会被触发。

它里面只有一个参数 args。写一下关键代码吧。

Pinia 与 VueX的区别与优缺点:

pinia和vuex的区别
(1)pinia它没有mutation,他只有state,getters,action【同步、异步】使用他来修改state数据
 (2)pinia他默认也是存入内存中,如果需要使用本地存储,在配置上比vuex麻烦一点

 (3)pinia语法上比vuex更容易理解和使用,灵活。
 (4)pinia没有modules配置,没一个独立的仓库都是definStore生成出来的

 (5)pinia state是一个对象返回一个对象和组件的data是一样的语法
 

Vuex 和 Pinia 的优缺点
Pinia的优点

完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
极其轻巧(体积约 1KB)
store 的 action 被调度为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数,这在 Vuex 中很常见
支持多个Store
支持 Vue devtools、SSR 和 webpack 代码拆分
Pinia的缺点

不支持时间旅行和编辑等调试功能  ?

Vuex的优点

  • 支持调试功能,如时间旅行和编辑
  • 适用于大型、高复杂度的Vue.js项目

Vuex的缺点

  • 从 Vue 3 开始,getter 的结果不会像计算属性那样缓存
  • Vuex 4有一些与类型安全相关的问题

何时使用Pinia,何时使用Vuex


个人感觉:,由于Pinea是轻量级的,体积很小,它适合于中小型应用。它也适用于低复杂度的Vue.js项目,因为一些调试功能,如时间旅行和编辑仍然不被支持。
将 Vuex 用于中小型 Vue.js 项目是过度的,因为它重量级的,对性能降低有很大影响。因此,Vuex 适用于大规模、高复杂度的 Vue.js 项目。
 

Vue3使用Vuex_vue3 vuex官网_BigJF的博客-CSDN博客

Vue3中Vuex的使用_vue3中使用vuex_普通网友的博客-CSDN博客

Vue3之Vuex_vue3的vuex_开longlong了吗?的博客-CSDN博客

Vue3中使用Pinia详解_九仞山的博客-CSDN博客

pinia和vuex的区别 Vuex 和 Pinia 的优缺点 何时使用Pinia,何时使用Vuex_pinia和vuex区别_more名奇妙的博客-CSDN博客

相关文章:

VueX 与Pinia 一篇搞懂

VueX 简介 Vue官方&#xff1a;状态管理工具 状态管理是什么 需要在多个组件中共享的状态、且是响应式的、一个变&#xff0c;全都改变。 例如一些全局要用的的状态信息&#xff1a;用户登录状态、用户名称、地理位置信息、购物车中商品、等等 这时候我们就需要这么一个工…...

指针与空间按钮的交互

文章目录 原理案例&#xff1a;“直线指针”和“点击按钮”的交互1、效果2、步骤 原理 指针不能直接和空间按钮交互&#xff0c;得借助一个中间层——分发器——它分发指针的进入、退出、选择事件&#xff0c;空间按钮自动监听这些事件 案例&#xff1a;“直线指针”和“点击…...

java八股文面试[数据库]——慢查询优化

分析慢查询日志 直接分析慢查询日志&#xff0c; mysql使用explain sql语句进行模拟优化器来执行分析。 oracle使用explain plan for sql语句进行模拟优化器来执行分析。 table | type | possible_keys | key |key_len | ref | rows | Extra EXPLAIN列的解释&#xff1a; ta…...

《Flink学习笔记》——第十章 容错机制

10.1 检查点&#xff08;Checkpoint&#xff09; 为了故障恢复&#xff0c;我们需要把之前某个时间点的所有状态保存下来&#xff0c;这份“存档”就是“检查点” 遇到故障重启的时候&#xff0c;我们可以从检查点中“读档”&#xff0c;恢复出之前的状态&#xff0c;这样就可以…...

【LeetCode-中等题】230. 二叉搜索树中第K小的元素

文章目录 题目方法一&#xff1a;层序遍历 集合排序方法二&#xff1a;中序遍历&#xff08;栈 或者 递归 &#xff09;方法三&#xff08;方法二改进&#xff09;&#xff1a;中序遍历&#xff08;栈 &#xff09; 题目 该题最大的特点就是这个树是二叉树&#xff1a; 所以…...

DQL语句的用法(MySQL)

文章目录 前言一、DQL语句间接和语法1、DQL简介2、DQL语法 二、DQL语句使用1、基础查询&#xff08;1&#xff09;查询多个字段&#xff08;2&#xff09;为字段设置别名&#xff08;3&#xff09;去除重复记录 总结 前言 本文主要介绍SQL语句中DQL语句的功能和使用方法&#…...

【Navicat Premium 16】使用Navicat将excel的数据进行单表的导入,详细操作

业务场景&#xff1a;经常与数据打交道嘛&#xff0c;有的时候会需要将excel的数据导入到数据库中&#xff0c;后面发现对于单表的数据导入&#xff0c;使用Navicat还是非常方便的&#xff0c;仅仅需要将字段关系映射好就可以了 一、开始操作 前提条件&#xff1a;已经成功连接…...

学习笔记230810--vue项目中get请求的两种传参方式

问题描述 今天写了一个对象方式传参的get请求接口方法&#xff0c;发现没有载荷&#xff0c;ip地址也没有带查询字符串&#xff0c;数据也没有响应。 代码展示 错误分析 实际上这里的query是对象方式带参跳转的参数名&#xff0c;而get方法对象方式传参的参数名是parmas 解…...

分享一种针对uni-app相对通用的抓包方案

PART1&#xff0c;前言 近年来混合开发APP逐渐成为主流的开发模式&#xff0c;与传统的开发模式相比混合开发极大的提升了开发效率&#xff0c;同时跨平台的特性也降低了开发成本&#xff0c;一直以来混合开发被诟病的性能问题随着技术的发展也得到改善。技术的发展往往是一把…...

【2023百度之星备赛】码蹄集 BD202301 公园(BFS求最短路)

题目 https://www.matiji.net/exam/brushquestion/1/4347/179CE77A7B772D15A8C00DD8198AAC74?from1 题目大意&#xff1a; 给定一个无向图&#xff0c;有两个人往同一个目的地走&#xff0c;分别消耗体力TE、FE。如果他们到某个点汇合了&#xff0c;然后一起走向目的地&…...

2022年下半年系统架构设计师真题(下午带答案)

试题一 (25分) 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。在项目立项之初&#xff0c;公司领导层一致认为本次升级的主要目标是提升会员管理方式的灵活性&#xff0c;由于当前用户规模不大&#xff0c;业务也相对…...

26、ADS瞬时波形仿真-TRANSIENT仿真(以共射放大器为例)

26、ADS瞬时波形仿真-TRANSIENT仿真&#xff08;以共射放大器为例&#xff09; 在本科期间&#xff0c;学习模电的时候总是要对各种三极管电路进行MULTISIM仿真&#xff0c;其实ADS具备相同的功能&#xff0c;而且对于射频电路&#xff0c;使用ADS进行仿真可以结合版图进行&am…...

【微服务部署】02-配置管理

文章目录 1.ConfigMap1.1 创建ConfigMap方式1.2 使用ConfigMap的方式1.3 ConfigMap使用要点建议 2 分布式配置中心解决方案2.1 什么时候选择配置中心2.2 Apollo配置中心系统的能力2.2.1 Apollo创建配置项目2.2.2 项目使用2.2.3 K8s中使用Apollo 1.ConfigMap ConfigMap是K8s提供…...

NTP时钟同步服务器

目录 一、什么是NTP&#xff1f; 二、计算机时间分类 三、NTP如何工作&#xff1f; 四、NTP时钟同步方式&#xff08;linux&#xff09; 五、时间同步实现软件&#xff08;既是客户端软件也是服务端软件&#xff09; 六、chrony时钟同步软件介绍 七、/etc/chrony.conf配置文件介…...

webassembly003 ggml GGML Tensor Library part-2 官方使用说明

https://github.com/ggerganov/whisper.cpp/tree/1.0.3 GGML Tensor Library 官方有一个函数使用说明&#xff0c;但是从初始版本就没修改过 : https://github1s.com/ggerganov/ggml/blob/master/include/ggml/ggml.h#L3-L173 This documentation is still a work in progres…...

ES主集群的优化参考点

因为流量比较大&#xff0c; 导致ES线程数飙高&#xff0c;cpu直往上窜&#xff0c;查询耗时增加&#xff0c;并传导给所有调用方&#xff0c;导致更大范围的延时。如何解决这个问题呢&#xff1f; ES负载不合理&#xff0c;热点问题严重。ES主集群一共有几十个节点&#xff0…...

全国范围内-二手房小区数据-2023-8月更新

收录融合去重多个平台数据&#xff1a;80万&#xff0c;仅供数字参考 数据纬度字段名注释枚举值基础信息id主键id&#xff1a;名称城市来源生成 md5值00001073838501125ec4473463ead9ccname名称瑞祥安文创园address地址(朝阳)双桥路东柳村口南口lng经度116.581903lat纬度39.89…...

第4章 循环变换

4.1 适配体系结构特征的关键技术 由于高级语言隐藏了底层硬件体系结构的大量细节&#xff0c;如果不经过优化直接将高级程序设计语言编写的程序部署在底层硬件上&#xff0c;往往无法充分利用底层硬件体系结构的处理能力。 算子融合不仅可以提…...

spring cloud使用git作为配置中心,git开启了双因子认证,如何写本地配置文件

问题 spring cloud使用git作为配置中心&#xff0c;git开启了双因子认证&#xff0c;死活认证不成功&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 报错关键字 org.eclipse.jgit.api.errors.TransportException: https://git.qualink.com/zhaoxin15/sc-confi…...

JVM内存管理、内存分区:堆、方法区、虚拟机栈、本地方法栈、程序计数器

内存管理 内存分区 线程共享 堆 存放实例&#xff0c;字符串常量&#xff08;直接引用&#xff09;&#xff0c;静态变量&#xff0c;线程分配缓冲区&#xff08;TLAB线程私有&#xff09;。垃圾收集器管理的区域 方法区 非堆&#xff0c;和堆相对的概念。存储已被虚拟机加载的…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...