vue3使用pinia模块化状态管理工具

pinia全局状态管理工具

我们可以看到在Vue3中,官方已经用Pinia代替了Vuex成为官方的状态管理库。pinia可以更好的支持ts的使用

pinia官网链接Pinia 中文文档

特点:

  • 支持Vue3 和 Vue2
  • 去除 mutations,只有 state,getters,actions;
  • actions 支持同步和异步;
  • 完整的 ts 的支持;
  • 无需再创建各个模块嵌套了,Vuex中如果数据过多,我们通常分模块来进行管理,稍显麻烦,而pinia中每个store都是独立的,互相不影响。
  • 足够轻量,压缩后的体积只有1kb左右;
  • 无需手动添加 store,store 一旦创建便会自动添加;
  • pinia支持插件来扩展自身功能。
  • 支持服务端渲染。

vue3建议使用pinia代替vuex进行状态管理

模块化

在大型项目实际开发中,不可能把多个模块的数据都定义到一个store中,而是一个模块对应一个store,最后通过一个根store进行整合,结构目录如下:

module文件夹用于存放每个不同模块的状态管理

index.js为store的入口文件,将每个模块导入并统一导出

1. 安装

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

2. 引入注册Vue3

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

vue2使用

import { createPinia, PiniaVuePlugin } from 'pinia'
 
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
 
new Vue({
  el: '#app',
  // other options...
  // ...
  // note the same `pinia` instance can be used across multiple Vue apps on
  // the same page
  pinia,
})

3. 创建store

store简单来说就是数据仓库的意思,我们数据都放在store里面。当然你也可以把它理解为一个公共组件,只不过该公共组件只存放数据,这些数据我们其它所有的组件都能够访问且可以修改

在src下面创建一个store文件夹,再创建与之对应的js文件,比如user.js

import { defineStore } from 'pinia'
​
// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useUser = defineStore('user', {
  // other options...
})

Store 是使用 defineStore() 定义的,接收两个参数:

  • name:一个字符串,必传项,该store的唯一id。
  • options:一个对象,store的配置项,比如配置store内的数据,修改数据的方法等等。

我们可以定义任意数量的store,因为我们其实一个store就是一个函数,这也是pinia的好处之一,让我们的代码扁平化了,这和Vue3的实现思想是一样的。

4. 使用 store

<script setup>
import { useStore } from '@/store/user.js'
const store = useStore()
console.log(store)
</script>

这是空store的输出内容,我们可以尝试依次添加state、getters、actions属性后观察store打印的内容

5. 添加state

import { defineStore } from 'pinia'

export const useStore = defineStore('user', {
    // 推荐使用 完整类型推断的箭头函数
    state: () => {
        return {
            // 所有这些属性都将自动推断其类型
            name: '',
            token: ''
        }
    },
})

添加state属性之后,可以看到,state里面return的属性添加到了store上,这时我们在获取时不需要在state上获取,而是直接在store上获取

请注意,store 是一个用reactive 包裹的对象,这意味着不需要在getter 之后写.value,但是,就像setup 中的props 一样,我们不能对其进行解构

5.1. 读取state

<script setup>
import { useStore } from '@/store/user.js'
const store = useUser()
console.log(store)
  // ❌ 这不起作用,因为它会破坏响应式
  // 这和从 props 解构是一样的
  const { name } = store  ❌
	// 需要调用storeToRefs方法
  const { uname } = storeToRefs(store) 
</script>

5.2. 重置状态

const store = useStore()

store.$reset()

5.3. 修改state

store.name='name'

5.4. 批量修改state

调用 $patch 方法。 它允许您使用部分“state”对象同时应用多个更改:

store.$patch({
  counter: store.counter + 1,
  name: 'Abalam',
})

但是,使用这种语法应用某些突变非常困难或代价高昂:任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。 正因为如此,$patch 方法也接受一个函数来批量修改集合内部分对象的情况:

store.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

5.5. 直接替换整个state(几乎不用)

// 替换
store.$state = { userName: 'rose' }

6. getter

6.1. 使用getter

类似于计算属性,他们接收“状态”作为第一个参数以鼓励箭头函数的使用:

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

获取

// 直接获取
<div>{{userStore.doubleCount}}</div>

6.2. getters方法中带参数

export const useStore = defineStore('main', {
  getters: {
    getUserById: (state) => {
      return (userId) => state.users.find((user) => user.id === userId)
    },
  },
})

调用

// 直接获取
<div>{{userStore.getUserById(1)}}</div>

6.3. 访问其他 getter

通过 this 访问到 整个 store 的实例

export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
  }),
  getters: {
    // 类型是自动推断的,因为我们没有使用 `this`
    doubleCount: (state) => state.counter * 2,
    // 这里需要我们自己添加类型(在 JS 中使用 JSDoc)。 我们还可以
    // 使用它来记录 getter
    /**
     * 返回计数器值乘以二加一。
     *
     * @returns {number}
     */
    doubleCountPlusOne() {
      // 自动完成 ✨
      return this.doubleCount + 1
    },
  },
})

6.4. 访问其他Store中的 getter

import { useOtherStore } from './other-store'

export const useStore = defineStore('main', {
  state: () => ({
    // ...
  }),
  getters: {
    otherGetter(state) {
      const otherStore = useOtherStore()
      return state.localData + otherStore.data
    },
  },
})

使用setup()

import { useCounterStore } from '../stores/counterStore'

export default {
  setup() {
    const counterStore = useCounterStore()

    return { counterStore }
  },
  computed: {
    quadrupleCounter() {
      return counterStore.doubleCounter * 2
    },
  },
}

没有setup()

您可以使用 previous section of state 中使用的相同 mapState() 函数映射到 getter:

import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'

export default {
  computed: {
    // 允许访问组件内的 this.doubleCounter
    // 与从 store.doubleCounter 中读取相同
    ...mapState(useCounterStore, ['doubleCount'])
    // 与上面相同,但将其注册为 this.myOwnName
    ...mapState(useCounterStore, {
      myOwnName: 'doubleCounter',
      // 您还可以编写一个访问 store 的函数
      double: store => store.doubleCount,
    }),
  },
}

7. Actions

前面我们提到的state和getters属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data数据和computed计算属性一样。
那么,如果我们有业务代码的话,最好就是写在actions属性里面,该属性就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。
actions属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。

7.1. 添加actions(此时的useUsersStore即是最基本的一个架构了)

import { defineStore } from 'pinia'
export const useUsersStore = defineStore('users', {
  // 其它配置
  state: () => {
    return {
      name: "李华",
      age: 25,
      sex: "男",
    }
  },
  getters: {
    getAddAge: (state) => {
      return state.age + 100;
    },
    getAddParamsAge: (state) => {
      return (params:number)=>state.age + 100 + params;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge;
    },
  },
  actions: {
    saveName(name: string) {
      // 修改state中的name
      this.name = name;
    },
  },
})

此处actions里常写复杂逻辑方法。举例仅为简单的直接修改了state的值。在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。大家把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store。

actions定义的方法同样在store直接可以访问到。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值