Pinia——Vue专属状态管理工具

本文介绍了在Vue3项目中如何使用Pinia创建和管理Store,包括安装、定义OptionStore和SetupStore、state、getter、action的使用,以及Pinia与Vuex的主要区别。
摘要由CSDN通过智能技术生成

目录

(一)介绍

(二)在vue3项目中安装pinia

(三)定义Store

1.Option Store

2.Setup Store

3.使用store

(四)State

1.state与ts

2.修改state中的状态

(五)Getter

1.基本使用

2.访问其他getter

3.向getter传递参数

4.访问其他store的getter

(六)Action

1.异步请求 

2.访问其他store的action

(七)pinia和vuex的区别


(一)介绍

如果使用 Pinia,即使在小型单页应用中,你也可以获得如下功能:

  • Devtools 支持
    • 追踪 actions、mutations 的时间线
    • 在组件中展示它们所用到的 Store
    • 让调试更容易的 Time travel
  • 热更新
    • 不必重载页面即可修改 Store
    • 开发时可保持当前的 State
  • 插件:可通过插件扩展 Pinia 功能
  • 为 JS 开发者提供适当的 TypeScript 支持以及自动补全功能。
  • 支持服务端渲染

Pinia更适合中、小型项目,大型项目还是更推荐使用vuex 

(二)在vue3项目中安装pinia

可以直接在创建vue3项目中选择是否使用pinia,这里展示的是手动安装方法。 

npm install pinia

创建一个 pinia 实例 (根 store) 并将其传递给应用

在main.js中:
// 引入pinia
import { createPinia } from 'pinia'
// 创建实例对象
const pinia = createPinia()
// 应用
app.use(pinia)

(三)定义Store

Store 是用 defineStore() 定义的,它的第一个参数必须是独一无二的名字,是应用中 Store 的唯一 ID,Pinia 将用它来连接 store 和 devtools。

对于defineStore()的返回值可以任意命名,但建议使用use开头,store结尾,中间写上应用该store模块的名字,如:useNameStore、useAlertsStore等。

defineStore()的第二个参数可接受两类值:Setup 函数或 Option 对象,即后续即将讲到的Option Store和Setup Store。

1.Option Store

与vue2的选项式api风格类似,传入一个带有state、getters、actions属性的对象

import { defineStore } from "pinia"

export const useAgeStore = defineStore('age', {
    state: () => ({ // state使用函数形式返回 防止数据发生污染
        age: 18
    }),
    getters: {
        doubleAge(state) {
            return state.age * 2
        }
    },
    actions: {
        addAge() {
            // this指针指向store
            this.age++
        }
    }
})

2.Setup Store

与vue3的组合式api的setup()函数类似

import { defineStore } from "pinia"
import { ref } from 'vue'

export const useNameStore = defineStore('name', () => {
    // state
    let name = ref('csq')
    // getter
    let doubleName = computed(() => {
        return name.value + '*'
    })
    // actions
    const addName = function () {
        name.value += '++'
    }
    // 将需要用到的数据、函数传出去
    return {
        name, doubleName, addName
    }
})

3.使用store

在 Setup Store 中:

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

在组件中引入store函数,直接使用 

import { useAgeStore } from "@/store/index";
const ageStore = useAgeStore()

<h2>{{ ageStore.age }}</h2>
<h2>{{ ageStore.doubleAge }}</h2>
<button @click="ageStore.addAge()">增加age</button>

如图,defineStore()返回的对象实际是用proxy进行代理的对象,因此state不需要加.value才能获取到state值 

store 是一个用 reactive 包装的对象,这意味着不需要在 getters 后面写 .value

 结构赋值:

利用es6的解构赋值可以将store里的state、getters、actions提取出来,但是得到的state和getters不是响应式数据。actions可以直接解构出来,因为它们也被绑定到 store 上。

为了从 store 中提取属性时保持其响应性,需要使用 storeToRefs(),在只需要使用到store的state和getters时非常有用

import { useNameStore } from "@/store/index";
import { storeToRefs } from "pinia";

const nameStore = useNameStore()
// const { name, doubleName } = nameStore // 这样解构出的数据不是响应式
// 借助storeToRefs()实现响应式引用
const { name, doubleName } = storeToRefs(nameStore)
const { addName } = nameStore

<h2>{{ name }}</h2>
<h2>{{ doubleName }}</h2>
<button @click="addName()">增加name</button>

(四)State

1.state与ts

State | Pinia

const useStore = defineStore('storeId', {
  state: () => {
    return {
      // 用于初始化空列表
      userList: [] as UserInfo[],
      // 用于尚未加载的数据
      user: null as UserInfo | null,
    }
  },
})

interface UserInfo {
  name: string
  age: number
}

2.修改state中的状态

方式1:直接修改 (不建议)

const personStore = usePersonStore()

const changeInfo = function () {
    // 方式1:直接修改 (不建议)
    personStore.name = 'zkj'
    personStore.age = 20
}

方式2:$patch(对象) 实现批量修改 (建议)

const personStore = usePersonStore()

const changeInfo = function () {
    // 方式2:$patch(对象) 实现批量修改 (建议)
    personStore.$patch({
        name: 'zkj',
        age: 20,
        arr: [...personStore.arr, 5] // 过于繁琐,且对数组进行其他操作不方便
    })
}

方式3:$patch(函数) 实现批量修改 (强烈建议)

const personStore = usePersonStore()

const changeInfo = function () {
    // 方式3 $patch(函数) 实现批量修改 (强烈建议)
    personStore.$patch((state) => { // 形参state 可获取store里的state
        state.name = 'zkj'
        state.age = 20
        state.arr.splice(2, 1)
    })
}

方式4:封装到actions函数中 (逻辑复杂时使用)

// 方式4:封装到actions函数中 (逻辑复杂时使用)
personStore.changeInfo()

(五)Getter

Getter | Pinia

1.基本使用

Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数 

export const usePersonStore = defineStore('person', {
    state: () => ({
        name: 'csq',
        age: 18
    }),
    getters: {
        // 箭头函数 第一个参数接收state(推荐写法)
        getterName: (state) => {
            return state.name + '*'
        },
}

<h2>{{ personStore.getterName }}</h2>

使用this指针获取store

注意:使用箭头函数会导致this指针无法指向store

getters:{
    // this指针指向store 但无法进行类型推断(ts),必须手动指明返回值的数据类型
        getterAge(): number {
            return this.age * 2
        },
}

<h2>{{ personStore.getterName }}</h2>

2.访问其他getter

getters:{
    // 利用this指针和state可以做到getters数据之间的交互
        getterAll(state): string {
            return state.name + this.getterAge
        },
}

<h2>{{ personStore.getterAll }}</h2>

3.向getter传递参数

getters:{
    // 返回函数 可以实现想getters传递参数
        // getter 将不再被缓存,它们只是一个被你调用的普通函数
        getterAge2: (state) => {
            return (data: number) => data + state.age
        },
}

<h2>{{ personStore.getterAge2(100) }}</h2>

4.访问其他store的getter

import { useCounterStore } from "./useCounterStore"

getters:{
    // 访问其他store的getter
        getterOther: (state) => {
            const counterStore = useCounterStore()
            return state.age + counterStore.count
        }
}

<h2>{{ personStore.getterOther }}</h2>

(六)Action

Action 相当于组件中的 method。它们可以通过 defineStore() 中的 actions 属性来定义,并且它们也是定义业务逻辑的完美选择。

action 可通过 this 访问整个 store 实例,不同的是,action 可以是异步的,你可以在它们里面 await 调用任何 API,以及其他 action

1.异步请求 

setup store写法: 

export const usePersonStore = defineStore('person', () => {
    let name = ref('zkj')
    let age = ref(18)
    // 异步获取person数据
    const getData = async function () {
        let res = await axios.get('http://localhost:5000/student')
        console.log(res);
        name.value = res.data.name // name是ref函数定义的数据
        age.value = res.data.age
    }

    return {
        name, age, getData
    }
})

option store写法:

export const usePersonStore = defineStore('person', {
    state: () => ({
        name: 'zkj',
        age: 18
    }),
    actions: {
        // 此时this指针指向store实例 只能使用普通函数,不能使用箭头函数!!
        async getData() {
            let res = await axios.get('http://localhost:5000/student')
            console.log(res);
            this.name = res.data.name
            this.age = res.data.name
        }
    }
})

2.访问其他store的action

setup store写法: 

// 访问其他store的action
    const getData = async function () {
        const counterStore = useCounterStore()
        if (counterStore.login()) { // 如果为真 执行数据请求命令
            let res = await axios.get('http://localhost:5000/student')
            console.log(res);
            name.value = res.data.name // name是ref函数定义的数据
            age.value = res.data.age
        }
    }

(七)pinia和vuex的区别

  • 与 Vuex 相比,Pinia 不仅提供了一个更简单的 API,也提供了符合组合式 API 风格的 API,最重要的是,搭配 TypeScript 一起使用时有非常可靠的类型推断支持
  • pinia 没有 mutations,而actions的使用不同,在actions中可以处理同步也可以处理异步,getters的使用是一致的
  • pinia 没有总出口全是模块化,需要定义模块名称,当多个模块需要协作的时候需要引入多个模块,vuex是有总入口的,在使用模块化的时候不需要引入多个模块
  • pinia 在修改状态的时候不需要通过其他api,vuex需要通过commit,dispatch去修改所以在语法上比vuex更容易理解和使用,灵活

pinia就是更好的vuex,在项目中可以直接使用它了,尤其是使用了TypeScript的项目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值