Vue3 Pinia

1.介绍

  • Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态,用来代替Vuex
  • 由来:
    • Vuex 在大型项目中容易变成一个“巨型中心仓库”,即使使用 modules,也存在配置复杂、命名空间繁琐等问题
    • Pinia 让每个业务模块拥有自己的“小仓库”,既独立又互通,真正做到了“高内聚、低耦合”

2.引入和安装

  • 全局引入和安装
import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

3.定义store

  • 使用 defineStore 定义一个 store

    推荐store名称使用 use... 这一类风格
    参数一是唯一标识
    参数二可以选择选项式(对象语法),也可以选择组合式(function)

import { defineStore } from 'pinia'

const useCounterStore = defineStore('counter',{})
  • 选项式
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0, name: 'Eduardo' }),
  getters: {
    doubleCount: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})
  • 组合式

组合式需要借用 vue 的 ref, computed等方法创造数据,并且最后需要 return 创建的数据

//store/counter.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(1)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})

4.使用 Store

  • 组合式
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()

</script>

<template>
    <main>
        <div>
            <button @click="store.count++">{{ store.count }}</button>
        </div>
        <div>
            <button @click="store.increment()">{{ store.count }}</button>
        </div>
        <h2>{{ store.doubleCount }}</h2>
    </main>
</template>
  • 选项式(未验证)
import { useUserStore } from '@/stores/user'

export default {
  setup() {
    const user = useUserStore()
    return { user }
  },
  // 之后通过 this.user.xxx 进行读取
}

5.Store 解构与响应式陷阱

  • 在使用 Pinia 时,一个 store 通常包含多个状态(state)、计算属性(getters)和方法(actions)。为了方便,我们可能想直接解构出某个状态,而不是每次都写 store.xxx
const store = useCounterStore()
const { count } = store // ❌ 危险!

❌问题:解构出来的 count 会丢失响应性
此问题仅影响 state 和 getters(数据属性),不影响 actions(函数本身不需要响应式)

  • Pinia 提供了 storeToRefs 工具函数,专门用于安全地解构 store 的响应式属性

storeToRefs()它不是“拷贝数据”,而是“创建指向同一响应式源的引用”,操作 count++ 会直接关联到 store.count

<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'

const store = useCounterStore()
const { count, doubleCount } = storeToRefs(store) // ✅ 响应式解构!

// 在 setup 或组合式函数中可直接使用
console.log(count.value) // 注意:返回的是 ref,需 .value

</script>

<template>
    <main>
        <div>
            <button @click="count++">{{ count }}</button>
        </div>
        <div>
            <button @click="store.count++">{{ store.count }}</button>
        </div>
    </main>
</template>

6.组合式Store

  • 解析官方案例:为什么store 相互使用时,不能直接在 setup 函数中直接互相读取对方的 state
import { ref } from 'vue'
import { defineStore } from 'pinia'

export const useX = defineStore('x', () => {
    console.log('useX setup')
    const y = useY()

    console.log('useX steup y.name', y.name)
    function doSomething() {
        // ✅ 读取 computed 或 action 中的 y 属性
        const yName = y.name
        // ...
    }

    console.log('useX setup after')
    return {
        name: ref('I am X'),
    }
})

export const useY = defineStore('y', () => {
    console.log('useY setup')
    const x = useX()

    //此时useX的setup还未执行完毕,没有return,拿不到其name
    console.log('useY steup x.name', x.name)
    function doSomething() {
        // ✅ 读取 computed 或 action 中的 x 属性
        const xName = x.name
        // ...
    }

    console.log('useY setup after')
    return {
        name: ref('I am Y'),
    }
})
<script setup lang="ts">
import { useX, useY } from '@/stores/some'

var storeX = useX()
</script>
  • 输出结果
useX setup
useY setup
useY setup x.name undefined
useY setup after
useX setup y.name I am Y
useX setup after

X 先被调用(入口),但 Y 先执行完;
所以 Y 在初始化时读不到 X 的状态(因为 X 还没 return),
而 X 在初始化后期能读到 Y 的完整状态(因为 Y 已经 return 了)

  • 结论:根本不会死循环,defineStore 内部有单例缓存机制 —— 每个 store 只初始化一次
posted @ 2024-07-10 16:22  ---空白---  阅读(100)  评论(0)    收藏  举报