Skip to content

pinia与vuex对比与使用剖析

vuex与pinia的区别

  • pinia的特点是采用ts编写的, 类型提示友好, 体积小, 使用简单
  • pinia去除了mutations, 它只有state, getters, actions(包括了同步和异步)
  • pinia同时支持conpositionApi同时也兼容optionsApi, 可以无痛将vue3代码直接迁移到pinia
  • Vuex中需要使用module来定义模块(嵌套问题), 树结构(module套module), vuex中的命名空间的概念(namespaced)。 整个数据定义树结构 $store.state.a.b.c.xxx (createNamespaceHelpers()), 所有的模块的状态会自动定义到跟模块上, 所以会出现模块覆盖跟状态.
  • vuex中只允许程序有有一个store
  • pinia可以采用多个store, store之间可以互相调用(扁平化), 不用担心命名冲突问题

pinia需要vue版本是3.3

pinia的使用示例

1 . 安装

bash
pnpm add pinia
# or 
npm install pinia

2. 使用

main.js

js
import {createPinia} from 'pinia';
import {createApp} from 'vue';
const pinia = createPinia()

const app = createApp()
app.use(pinia)

app.mount('#app')

src/store/counter.jsApp.vue

src/store/counter.js

js
import {defineStore} from 'pinia';
export const useCounterStore = defineStore('counter', {
    state:{ // --> reactive({count:0})
        count: 0
    },
    getters:{ // -->computed
        double() {
            return this.count * 2
        }
    },
    actions:{ // --> methods
        add(payload) {
            this.count += payload
        }
    }
})

App.vue

vue
<script setup>
    import { useCountStore } from './store/counter';
    const store = useCountStore()
</script>

<template>
<div>
 计数器:{{store.count}}    
</div>双倍:{{store.double}}</div>
<button @click="store.add(2)">累加</button>
</template>

注意:

  1. pinia中的state其实就是一个响应式对象, 因此可以直接在组件中直接修改state的值, 但是不建议

  2. pinia中defineStore方法, 支持optionsApi和componsitionsApi,

    • 若是componsitionsApi, 则第一个参数是storeId, 一个唯一的名字, 第二个参数是一个setup函数, 这个setup函数其实就是组件中的setup函数
    js
    import { defineStore } from 'pinia';
    import { ref, computed } from 'vue';
    export const useCountStore = defineStore('counter', ()=>{
        const state = ref(0)
        const double = computed(()=>{
            return state.value * 2
        })
        const add = (payload)=>{
            return state += payload
        }
        
        return {
            state, double, add
        }
    })
    • 若是optionsAPI, 则可以个参数可以是storeId, 第二个参数是一个对象, 如src/store/counter.js
    • optionsAPI第一个参数也可以是一个对象, 但是里面得有一个id属性, 值为storeId(store名字, 全局唯一), 其余的属性就是state, getters, actions, 没有mutations
    js
    import { defineStore } from 'pinia';
    export const useCounterStore = defineStore({
        id:'counter',
        state:{
            count: 0
        },
        getters:{
            double() {
                return this.count * 2
            }
        },
        actions:{
            add(payload) {
                this.count += payload;
            }
        }
    })

剖析pinia的实现

vue3中若想某个属性所有的组件都能使用

vue.config.globalPrototype.$xxx = xxx ,这样所有组件都能通过$xxx来访问, 因为vue3不再是类封装的

例如:

js
const pinia = {
    install(app) {
        // 我们期望所有组件都可以访问到这个pinia
        app.config.globalProtptypes.$pinia = pinia
        // Vue2是将$pinia挂载到Vue.prototype上
        // Vue.prototype.$pinia = pinia
        
        // Vue3可以通过inject注入使用
        app.provide(PiniaSymbol, pinia)
    }
}

createPinia内部会将项目中每一个store都注册进app内, 在defineStore方法中检测是否有注册过store, key是storeId, value 是对应的store, 若注册过则不再注册, 而是直接取用, 反之则先注册, 注册后写入store 存储的Map中, 再直接取用