VueX 与Pinia 一篇搞懂

news2024/10/7 12:21:26

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

启莱OA treelist.aspx SQL注入

子曰&#xff1a;“为政以德&#xff0c;譬如北辰&#xff0c;居其所&#xff0c;而众星共之。” 漏洞复现 访问漏洞url&#xff1a; 使用SQLmap对参数 user 进行注入 漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感…

Java“牵手”1688淘口令转换API接口数据,1688API接口申请指南

1688平台商品淘口令接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取1688商品的标题、价格、库存、商品快递费用&#xff0c;宝贝ID&#xff0c;发货地&#xff0c;区域ID&#xff0c;快递费用&#xff0c;月销量、总销量、库存、详情描…

juicefs源码format命令阅读

之前博文中介绍过在windows下安装GO和vscode windows下安装go环境 和vscode中go扩展调试 1、获取源码 git clone https://github.com/juicedata/juicefs.git 首先观察代码架构 上图是我已经编译过得代码&#xff0c;可能和刚git下来的有些出入。 2、编译 我是在windows上进…

C++学习笔记总结练习:运算符重载两种方式

运算符重载的两种方式 1 基本概念 基础 运算符时具有特殊名字的函数&#xff1a;由关键字operator和气候定义的运算符共同组成。 可以被重载的运算符 方式 将运算符重载为类的成员函数。重载运算符函数&#xff0c;并声明为类的友元。 规则 重载后的运算符必须至少有一个…

可控硅调功电路原理

在常见的马达调速以及需要调整负载功率的场合&#xff0c;经常会用到可控硅调功电路&#xff0c;下图是常见的应用电路。 调功电路主要由阻容移相电路和可控硅触发电路构成&#xff0c;工作过程如下&#xff0c;当交流电的正半周时&#xff0c;交流电通过R5,可调电阻R3给电容C1…

Elasticsearch数据库操作

索引操作 新建索引 PUT /ztt {"mappings": {"properties": {"info":{"type": "text","analyzer": "ik_smart"},"email":{"type": "keyword","index": false…

简单聊聊Https的来龙去脉

简单聊聊Https的来龙去脉 Http 通信具有哪些风险Https Http SSL/TLS对称加密 和 非对称加密数字证书数字证书的申请数字证书怎么起作用 Https工作流程一定需要Https吗&#xff1f; Http 通信具有哪些风险 使用明文通信&#xff0c;通信内容可能会被监听不验证通信双方身份&a…

每天刷题五道RHCSA/6-10题(Radhat8.2)

6.创建协作目录权限 mkdir /home/managers chown :sysmgrs /home/managers chmod 2770 /home/managers 测试&#xff1a; touch /home/managers/12345 ll /home/managers/12345 7.配置NTP systemctl status chronyd #查看状态 yum -y install chrony #如果没有安装&#xff0c…

最佳实践:TiDB 业务读变慢分析处理

作者&#xff1a;李文杰 网易游戏计费 TiDB 负责人 在使用或运维管理 TiDB 的过程中&#xff0c;大家几乎都遇到过 SQL 变慢的问题&#xff0c;尤其是查询相关的读变慢问题。读变慢的问题大部分情况下都遵循一定的规律&#xff0c;通过经验的积累可以快速的定位和优化&#xff…

Java实现根据关键词搜索京东商品列表数据方法,当当API接口(jd.item_search)申请指南

要通过京东网的API获取商品列表数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取商品列表&#xff1a; 首先&#xff0c;确保您已注册成为当当开放平台的开发者&#xff0c;并创建…

五金轴尺寸机器视觉测量软硬件方案--康耐德智能

检测内容&#xff1a; 五金轴尺寸机器视觉测量 检测要求&#xff1a; 精度0.015mm&#xff0c;速度180~240个/分钟 视觉可行性分析&#xff1a; 对样品进行了光学实验&#xff0c;并进行图像处理&#xff0c;原则上可以使用机器视觉系统进行测试测量。 结果&#xff1a; 对…

新手小白想要做好跨境电商独立站,需要考虑哪些要素?

对于不少中小卖家而言&#xff0c;利用独立站出海已然成为下一个跨境热潮。但是采用独立站模式做出海生意前&#xff0c;卖家需要考虑哪些要素&#xff1f; 产品选择 对于国内的卖家来说&#xff0c;依托于国内强大的供应链优势&#xff0c;只要能把握住消费者心态&#xff0…

C# 获取Windows系统版本注意事项

首先通过微软官方文档&#xff1a;https://learn.microsoft.com/zh-cn/windows/win32/sysinfo/operating-system-version了解各个操作系统对应的版本号 下面介绍3种获取版本号的方式及弊端 1. Environment.OSVersion.Version OperatingSystem os Environment.OSVersion;// 判断…

Dapper入门教程

什么是Dapper Dapper是一个简单的.NET对象映射器&#xff0c;在速度方面具有"King of Micro ORM"的头衔&#xff0c;几乎与使用原始的ADO.NET数据读取器一样快。ORM是一个对象关系映射器&#xff0c;它负责数据库和编程语言之间的映射。 Dapper通过扩展IDbConnecti…

PSP - 蛋白质结构预测 OpenFold Multimer 模型训练参数与配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132575709 OpenFold Multimer 是用于预测蛋白质多聚体结构的计算方法。基于OpenFold 的单体预测框架&#xff0c;利用深度学习技术&#xff0c;结…

指针与空间按钮的交互

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

SQLServer审计功能配置

一. SQL Server审计功能介绍 SQL Server审计功能&#xff08;Audit&#xff09;是SQL Server 2008之后才有的功能&#xff0c;审计(Audit)用于追踪和记录SQL Server实例&#xff0c;或者单个数据库中发生的事件(Event)&#xff0c;审计运作的机制是通过捕获事件(Event)&#x…

Vector 动态数组(迭代器)

C数据结构与算法 目录 本文前驱课程 1 C自学精简教程 目录(必读) 2 Vector<T> 动态数组&#xff08;模板语法&#xff09; 本文目标 1 熟悉迭代器设计模式&#xff1b; 2 实现数组的迭代器&#xff1b; 3 基于迭代器的容器遍历&#xff1b; 迭代器语法介绍 对迭…

3分钟上手Python的命令行参数解析

1 简介 处理命令行参数对许多程序来说都是必不可少的功能。今天为大家介绍 Python 中的 argparse 模块&#xff0c;它是 Python 标准库中的一个模块&#xff0c;它可以让开发者非常简单地为程序添加命令行参数接口&#xff0c;包括位置参数、可选参数、标志等&#xff0c;并自…

测试左移——代码审计SonarQube 平台搭建

一、sonarqube代码分析技术体系 1、代码分析工具 IDE 辅助功能 xcode、android studio阿里巴巴 java 开发手册 ide 插件支持 独立的静态分析工具 spotbugs、findbugs、androidlint、scan-build、Checkstyle、FindSecBugspmd 阿里巴巴 java 开发手册 pmd 插件 综合性的代码…