# 核心概念

  Vue 的官方调试工具 Devtools (opens new window) (opens new window)已经集成了 Vuex,提供了诸如零配置的 Time-travel 调试、Revert 回退、Commit 提交(会影响 Base State)、状态快照、导入导出等高级调试功能(一定要亲自 ✍ 体验)。如图所示:

  我们可以通过下面的这个小案例,快速入手 vuex 的使用:

代码示例
  • stores / shop.ts
import { defineStore } from 'pinia'

interface InterGoods {
  name: string
  price: number
  num?: number
  select?: boolean
}

const useShopStore = defineStore('shop', {
  state() {
    return {
      goods: [
        { name: 'Apple', price: 2.5 },
        { name: 'Banana', price: 1.5 },
        { name: 'Orange', price: 3.0 },
        { name: 'Grapes', price: 4.5 },
        { name: 'Mango', price: 5.0 }
      ] as InterGoods[],
      isAllSelect: false
    }
  },

  getters: {
    total(): number {
      return this.goods
        .filter((item) => item.select)
        .reduce(function (prev: number, item) {
          return (prev += item.price * (item.num || 1))
        }, 0)
    }
  },

  actions: {
    updataNum(index: number, num: number) {
      this.goods[index].num = this.goods[index].num || 1

      this.goods[index].num! += num
    },
    allSelectChange() {
      this.goods.forEach((item) => {
        item.select = this.isAllSelect
      })
    },
    sigleChange() {
      this.isAllSelect = this.goods.every((item) => {
        return item.select === true
      })
      // console.log(reusult)
    }
  }
})

export default useShopStore
  • App.vue
<script setup lang="ts">
import useShopStore from './stores/shop'

const shopStore = useShopStore()

function updataNum(index: number, num: number) {
  shopStore.updataNum(index, num)
}

function allSelectChange() {
  shopStore.allSelectChange()
}

function sigleChange() {
  shopStore.sigleChange()
}
</script>

<template>
  <div>简约购物车案例</div>

  <div>
    全选:<input type="checkbox" v-model="shopStore.isAllSelect" @change="allSelectChange" />
  </div>

  <div v-for="(item, index) in shopStore.goods">
    <input @change="sigleChange()" type="checkbox" v-model="item.select" />
    商品名:{{ item.name }} --- 商品价格:{{ item.price }} ---
    <!-- num为0是,直接赋予它为 1 -->
    <button @click="updataNum(index, 1)">+</button>
    {{ item.num || 1 }}
    <button @click="updataNum(index, -1)">-</button> --- 小计:{{ item.price * (item.num || 1) }}
  </div>

  <div>总计:{{ shopStore.total }}</div>
</template>

# 1、State

  在使用 state 时,我们还可以通过 store 提供的方法对其进行 🤔 操作

State 与 Ts
interface UserInfo {
  name: string
  age: number
}

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

export default useStoreStore
<template>
  <div>你好,我是pinia ---{{ count }}</div>
  <buttom @click="userStore.$reset()">重置 state</buttom>

  <button @click="count++">直接修改 state</button>
  <buttom @click="changeState()">变更 state</buttom>
</template>

<script setup>
import useUserStore from './stores/counter'
import { storeToRefs } from 'pinia'

const userStore = useUserStore()
const { count, name, age } = storeToRefs(userStore)

function changeState() {
  // 方式1:直接修改
  // userStore.name = 'zhangsan'
  // userStore.age = 99

  // 方式2:使用 $patch
  userStore.$patch({
    name: 'zhangsan',
    age: 99
  })
}
</script>

# 2、Getter

  在 defineStore 中,我们可以通过 this 访问到整个 store 实例:

这样当我们在 getter 中使用另一个 getter 时,就不用像 vuex 一样需要第二个参数 getter,而是直接使用 this

注意

在 Pinia 中,state 和 getter 一样,在使用时并不需要和 vuex 一样加上.state.getter 用来区分

const useCarterStore = defineStore('carter', {
  state() {
    return {
      listArr: [
        { id: 1, name: '华为', product: '手机', hasStock: false },
        { id: 2, name: '小米', product: '家居', hasStock: true },
        { id: 3, name: '格力', product: '空调', hasStock: true }
      ]
    }
  },
  getters: {
    titleMsg(state) {
      return state.listArr.map((item) => item.name + '--' item.product)
    }

    // 使用this
    hasStock(state) {
      return state.listArr.filter((item) => item.hasStock)
    },
    stockProd() {
      return this.hasStock.map(item => item.product)
    }

    // 返回回调函数
    getProdDetails(state) {
      return function(id) {
        return state.listArr.find((item) => item.id === id)
      }
    }
    // {{ carterStore.getProdDetails(2) }} 使用示例
  }
})

export default useCarterStore
拓展:跨 store ✍ 的 getter 操作
import { defineStore } from 'pinia'
import useUserStore from './useUser'

const useCarterStore = defineStore('carter', {
  state() {
    return {
      listArr: [
        { id: 1, name: '华为', product: '手机', hasStock: false },
        { id: 2, name: '小米', product: '家居', hasStock: true },
        { id: 3, name: '格力', product: '空调', hasStock: true }
      ]
    }
  },
  getters: {
    showMsg(state) {
      const userStore = useUserStore()

      const listItem = state.listArr.find((item) => item.id === userStore.prodId)

      return userStore.name + 'has' + listItem.product
    }
  }
})

export default useCarterStore

# 3、Action

  在 Pinia 中,Action 基本就不用说了,想干啥就干啥(一家独大,又没有 mutation)

前面描述的使用 this、跨 store 操作等在 Action 中同样可以

const dely = () => {
  return new promise((resolve,reject) => {
    setTimeout(()=>{
      resolve()
    },1000)
  })
}

const useCounterStore = defineStore('main', {
  state() {
    return {
      count: 0,
    }
  },
  actions: {
    increment() {
      this.count++,
    },
    randomizeCounter() {
      this.count = Math.round(100 * Math.random())
    },
    // 使用函数传参
    async saveCount(payload) {
      await dely()
      this.count = payload
    }
  }
})

export default useCounterStore

# 4、插件

Pinia 持久化插件

  下面,我们直接写一下看看:

Pinia 插件是一个函数,可以选择性地返回要添加到 store 的属性。它接收一个可选参数,即 context。

export function myPiniaPlugin(context) {
  context.pinia // 用 `createPinia()` 创建的 pinia。
  context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
  context.store // 该插件想扩展的 store
  context.options // 定义传给 `defineStore()` 的 store 的可选对象。
  // ...
}

  以官网的 Pinia 插件案例为例:

    更新于 : 8/7/2024, 2:16:31 PM