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部分:
-
字符串的事件类型 (type)
-
一个**回调函数(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 上下文对象,可以理解为store
sum_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 上下文对象拿到 count
if (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.5501
moreAgestu_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获取state
let femalePersons = computed(() => store.state.female.personList) //直接获取state
function 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.info
function 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// 类型为 moduleAState
store.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,具体步骤如下
- 在src文件夹下新建store文件夹,后面所有涉及需要Pinia进行状态管理的代码都放在该文件夹下
- 在store文件夹下新建movieListStore.js文件,创建完成后,打开该文件
- 在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')
- 在需要获取状态的地方通过上面定义的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项目
- 从 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博客