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 上下文对象,可以理解为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,具体步骤如下

  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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/97084.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【二等奖方案】大规模金融图数据中异常风险行为模式挖掘赛题「Aries」解题思路

第十届CCF大数据与计算智能大赛&#xff08;2022 CCF BDCI&#xff09;已圆满结束&#xff0c;大赛官方竞赛平台DataFountain&#xff08;简称DF平台&#xff09;正在陆续释出各赛题获奖队伍的方案思路&#xff0c;欢迎广大数据科学家交流讨论。 本方案为【大规模金融图数据中…

查局域网所有占用IP

查局域网所有占用IP 按&#xff1a;winr 出现下面界面&#xff0c;在文本框中输入 cmd 按确定即可出现cmd命令界面 在cmd命令窗口输入你想要ping的网段&#xff0c;下面192.168.20.%i即为你想要ping的网段&#xff0c;%i代表0-255 for /L %i IN (1,1,254) DO ping -w 1 -n 1…

尚硅谷SpringMVC

五、域对象共享数据 1、使用ServletAPI向request域对象共享数据 首页&#xff1a; Controller public class TestController {RequestMapping("/")public String index(){return "index";} } <!DOCTYPE html> <html lang"en" xmln…

十五、模板方法模式

一、什么是模板方法模式 模板方法&#xff08;Template Method&#xff09;模式的定义如下&#xff1a;定义一个操作中的算法骨架&#xff0c;而将算法的一些步骤延迟到子类中&#xff0c;使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 模板方法模式包含以…

Sharding-JDBC(九)5.3.0版本,实现按月分表、自动建表、自动刷新节点

目录 一、简介二、Maven依赖三、配置文件application.ymlsharding.yaml 四、代码实现1.自动建表、自动刷新节点思路2.创建表结构3.TimeShardingAlgorithm.java 分片算法类4.ShardingAlgorithmTool.java 分片工具类5.ShardingTablesLoadRunner.java 初始化缓存类6.SpringUtil.ja…

postgresql-子查询

postgresql-子查询 简介派生表IN 操作符ALL 操作符ANY 操作符关联子查询横向子查询EXISTS 操作符 简介 子查询&#xff08;Subquery&#xff09;是指嵌套在其他 SELECT、INSERT、UPDATE 以及 DELETE 语句中的 查询语句。 子查询的作用与多表连接查询有点类似&#xff0c;也是为…

学习JAVA打卡第四十五天

StringBuffer类 StringBuffer对象 String对象的字符序列是不可修改的&#xff0c;也就是说&#xff0c;String对象的字符序列的字符不能被修改、删除&#xff0c;即String对象的实体是不可以再发生变化&#xff0c;例如&#xff1a;对于 StringBuffer有三个构造方法&#xff…

红黑树及其应用介绍(万字长文)

红黑树 定义与性质 红黑树是一种特殊的二叉查找树&#xff0c;它遵循了特定的规则使得其具有了平衡性。红黑树的定义包括以下几个方面&#xff1a; 每个节点要么是红色&#xff0c;要么是黑色。根节点是黑色的。每个叶子节点&#xff08;NIL节点&#xff0c;空节点&#xff…

【LeetCode题目详解】第八章 贪心算法 part06 738.单调递增的数字 968.监控二叉树 (day37补)

本文章代码以c为例&#xff01; 一、力扣第738题&#xff1a;单调递增的数字 题目&#xff1a; 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数…

zookeeper介绍、zookeeper的安装与配置

1、zookeeper介绍 1.1 官网说明 官方地址&#xff1a;http://zookeeper.apache.org/ 它是拿来管理 Hadoop、Hive、Pig的管理员&#xff0c; Apache Hbase和Apache Solr以及阿里的Dubbo等项目中都采用到了Zookeeper。 一句话&#xff1a;ZooKeeper是一个分布式协调技术、高性…

MySQL基础入门

推荐查看 数据库相关概念 MySQL百度百科 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Databa…

学习pytorch7 神经网络的基本骨架--nn,module的使用

神经网络的基本骨架--nn,module的使用 官网Module介绍Python父类子类继承关系前向神经网络pycharm快捷键重写类方法codedebug B站小土堆视频学习笔记 官网Module介绍 https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module Python父类子类继承关系…

自动化运维:Ansible脚本之playbook剧本

目录 一、理论 1.playbooks 2.YAML 3.使用ansible批量安装apache服务 4.定义、引用变量 5.指定远程主机sudo切换用户 6.when条件判断 7.迭代 8.Templates 模块 9.tags 模块 10.Roles 模块 二、实验 1.使用ansible批量安装apache服务 2.定义、引用变量…

对称加密 非对称加密 AC认证 https原理

文章目录 对称加密及漏洞非对称加密及漏洞什么是数据摘要&#xff08;也称数据指纹&#xff09;什么是CA认证CA证书签发过程https通信方案 对称加密及漏洞 对称加密是一种加密算法&#xff0c;使用相同的密钥&#xff08;也称为对称密钥&#xff09;用于加密和解密数据。在对称…

c++ lambda

Lambda Lambda 表达式一般用于定义匿名函数&#xff0c;使得代码更加灵活简洁&#xff0c;优点&#xff1a; 声明式编程风格&#xff1a;就地匿名定义目标函数或函数对象&#xff0c;不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序&#xff0c;好的可读性和可…

Spring与Mybatis集成且Aop整合

目录 一、集成 1.1 集成的概述 1.2 集成的优点 1.3 代码示例 二、整合 2.1 整合概述 2.2 整合进行分页 一、集成 1.1 集成的概述 集成是指将不同的组件、部分或系统组合在一起&#xff0c;以形成一个整体功能完整的解决方案。它是通过连接、交互和协调组件之间的关系来实…

python的异步编程async

一、介绍 在Python 3.5中引入了async和await关键字&#xff0c;用于异步编程。async关键字用于定义一个协程&#xff08;coroutine&#xff09;&#xff0c;而await关键字则用于等待一个协程完成。 注&#xff1a;协程&#xff08;coroutine&#xff09;是是一种轻量级的线程…

LeetCode——回溯篇(三)

刷题顺序及思路来源于代码随想录&#xff0c;网站地址&#xff1a;https://programmercarl.com 目录 46. 全排列 47. 全排列 II 332. 重新安排行程 51. N 皇后 37. 解数独 46. 全排列 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任…

Linux命令查看CPU、内存、IO使用情况简单介绍

文章目录 1. CPU相关介绍1.1 物理CPU1.2 物理CPU内核1.3 逻辑CPU1.4 几核几线程1.5 CPU设计图 2. top 查看系统负载、CPU使用情况2.1 系统整体的统计信息2.2 进程信息2.3 top命令使用 3. lscpu 显示有关 CPU 架构的信息4. free 查看内存信息5. iostat 查看io信息 1. CPU相关介绍…

雅思写作 三小时浓缩学习顾家北 笔记总结(一)

目录 饥饿网翻译100个句子记录 There are some economically deprived communities in large cities. there is no clear link between grouping student by ability and their levels of attainment. young people without tertiary education qualification normally hav…