摸鱼的功夫偷偷学了一手pinia

1,546 阅读4分钟

🦋 之前出过一篇文章讲过 vuex,如今 pinia 是 vue 官方正式的状态库,适用于 vue2 和 vue3。

初识 pinia

首先,pinia.js 是新一代的状态管理器,是由vuejs团队的成员进行开发的~
因此也被任务是下一代的vuex,也就是 vuex5🐳
Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了 Vuex 5 核心团队讨论中的许多想法。最终,我们意识到 Pinia 已经实现了我们在 Vuex 5 中想要的大部分内容,并决定实现它取而代之的是新的建议。

先讲讲 pinia 和 vuex 的区别

  1. id 是必要的,pinia 的每一个 store 模块中必须定义一个唯一的id值
  2. 创建方式: new Vuex.Store(...) (vuex3) ,createStore(...)(vuex4)
  3. state 是一个函数返回对象:state:() => ({...})
  4. 没有mutations!

pinia 和 vuex 相比的优势

相比于之前的 vuex,pinia 有了以下优势:

  1. pinia 的写法更简单,代码更清晰,支持 optionsApi 和 compositionApi 语法
  2. 更完善的Typescript 支持,无需创建自定义复杂的包装类型来支持 Typescript,所有的内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断
  3. 轻量级,只有1+kb的大小
  4. 无嵌套结构,可以在任意的sotre之间交叉组合使用

pinia 和 vuex 在使用上的不同

pinia 在 vue3 中的写法和用法:

// store.js
import { defineStore } from 'pinia'
export const GlobalStore = defineStore({
    id:'myGlobalStore', // 必填,在sotre中得是唯一的
    state:()=> ({
        name:'张三'
    }),
    getters:{},
    actions:{
        setName(val) {
            this.name = val;
        }
    },
})
// vue3中使用
<template>
    {{ name }}
    <button @click="btn">按钮</button>
</template>
<script setup>
    import { GlobalStore } from '@/store/store.js'
    let store = GlobalStore()
    let name = computed(() =>store.name)
    const btn = () => {
        store.setName('李四')
    }
</script>

vuex(4) 在 vue3 中的写法和用法:

// store.js
import { createStore } from 'vuex'
export default createStore({
    state: {name:'张三'},
    mutations:{
        setName (state,val){
            state.name = val;
        }
    },
    actions:{},
    getters:{}
})
// vue3 页面
<template>
    {{ name }}
    <button @click="btn">按钮</button>
</template>
<script setup>
import { useStore } from 'vuex'
let store = useStore
let name = computed(()=> store.state.name)
const btn = () => {
    store.commit('name','李四')
}
</script>

可以看得出来,pinia的写法更加简洁轻便,pinia取消了原有的mutations,且在我们取值、用方法的时候可以不用通过.state.commit就能获取到值或方法 🤩

安装

npm install pinia --save

什么是 store

一个 store 是一个实体,它持有未绑定到组件树的状态和业务逻辑。换句话说,它托管全局状态。它有点像一个始终存在并且每个人都可以读取和写入的组件。
它包含三个概念,stategetteraction,并且可以安全地假设这些概念等同于组件中的datacomputedmethods

创建 store

在 src/store 下新建 index.ts,导出 store

// src/store/index.ts

import { createPinia } from 'pinia'

const store = createPinia()

export default store

State

定义 State

在 src/store 下面创建一个 user.ts

// src/store/user.ts

import { defineStore } from 'pinia'

export const useUserStore = defineStore({
  id: 'user', // id 必填,且需要唯一
  state: () => {
    return {
      name: '张三'
    }
  }
})
获取 state
// 页面
<template>
  <div>{{ userStore.name }}</div>
</template>

<script lang="ts" setup>
import { useUserStore } from '@/store/user'

const userStore = useUserStore()
</script>

也可以结合 computed 获取 ~

const name = computed(() => userStore.name)

state 也可以用 es 的解构,和 props 一样解构会使其失去响应式,可以使用 toRefs 方法进行解构,也可以使用 pinia 提供的 storeToRefs 方法:

import { storeToRefs } from 'pinia'
const { name } = storeToRefs(userStore)
修改 state

和之前的 vuex 一样,可以直接修改:

import { useUserStore } from '@/store/user'
userStore.name = '李四'

但是我们一般不建议这么做 😠😡
和 vuex 一样,我们需要通过 方法(action)来修改 state,至于为什么不建议这么直接修改:一是难维护,在组件繁多情况下,一处隐蔽state更改,整个开发组帮你排查;二是破坏store封装,难以移植到其他地方。所以,为了你的声誉和安全着想,请停止游离之外的coding!!
action 里可以通过 this访问:

// src/store/user.ts

export const useUserStore = defineStore({
    id: 'user',
    state: () => {
        return {
            name: '张三'
        }
    },
    actions: {
        setName(val){
            this.name = val
        }
    }
})
// 页面
<script setup>
    import { useUserStore } from '@/store/user'
    const userStore = useUserStore()
    userStore.setName('李四')
</script>

Getters

这个没啥说的,和 vuex 的使用基本一致 😬

// src/store/user.ts

export const useUserStore = defineStore({
 id: 'user',
 state: () => {
   return {
     name: '张三'
   }
 },
 getters: {
   fullName: (state) => {
     return state.name + '丰'
   }
 }
})
// 页面调用
userStore.fullName

Actions

这里讲一讲同一个store中 actions 的相互调用

// src/store/user.ts

export const useUserStore = defineStore({
    id: 'user', 
    actions: {
       async login(username,password){
           const { data } = await axios.login(username,password)
           this.ecoName(data)
           return data
       },
       ecoName(data){
           console.log(data)
       }
    }
})

如果要调用其他 store 中的 action 要怎么做呢?引入对应的 store 后就可以直接访问其内部的方法了!
假设现在手里有个 app store

// src/store/app.ts
export const useAppStore = defineStore({
  id: 'app',
  actions: {
    setData(data) {
      console.log(data)
    }
  }
})

想在 user store 中使用

// src/store/user.ts

import { useAppStore } from './app'
export const useUserStore = defineStore({
  id: 'user',
  actions: {
    async login(account, pwd) {
      const { data } = await api.login(account, pwd)
      const appStore = useAppStore()
      appStore.setData(data) // 调用 app store 里的 action 方法
      return data
    }
  }
})

参考文章:
juejin.cn/post/704919…
juejin.cn/post/706811…
juejin.cn/post/708153…