1. 什么是Pinia
Pinia 是 Vue 的专属的最新状态管理库 ,是 Vuex 状态管理工具的替代品
特点优势:
- 提供更加简单的API(去掉了mutation)
- 提供符合组合式风格的API(和Vue3新语法统一)
- 去掉modules的概念,每一个store都是一个独立的模块
- 配合TypeScript更加友好,提供可靠的类型推断
2. 手动添加Pinia到Vue项目
后面在实际开发项目的时候,Pinia可以在项目创建时自动添加,现在我们初次学习,从零开始:
-
使用 Vite 创建一个空的 Vue3项目
npm init vite@latest
-
按照官方文档安装 pinia 到项目中
yarn add pinia # 或者使用 npm npm install pinia
创建一个 pinia 实例 (根 store) 并将其传递给应用(main.js):
import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' const pinia = createPinia() // 创建pinia实例 const app = createApp(App) // 创建根实例 app.use(pinia) // pinia插件的安装配置 app.mount('#app') // 视图的挂载
3. Pinia基础使用
- 定义store
- 组件使用store
4. getters实现
Pinia中的 getters 直接使用 computed函数 进行模拟, 组件中需要使用需要把 getters return出去
5. action异步实现
方式:异步action函数的写法和组件中获取异步数据的写法完全一致
-
接口地址:http://geek.itheima.net/v1_0/channels
-
请求方式:get
-
请求参数:无
需求:在Pinia中获取频道列表数据并把数据渲染App组件的模板中
store/channel.js
import { defineStore } from 'pinia'
import { ref } from 'vue'
import axios from 'axios'
export const useChannelStore = defineStore('channel', () => {
// 声明数据 state
const channelList = ref([])
// 声明操作数据的方法 action
const getList = async () => {
// 支持异步
const { data: { data } } = await axios.get('http://geek.itheima.net/v1_0/channels')
channelList.value = data.channels
}
// 声明getters相关
return {
channelList,
getList
}
})
App.vue
<script setup>
import { useChannelStore } from '@/store/channel'
const channelStore = useChannelStore()
</script>
<template>
<div>
<h3>App.vue根组件</h3>
<button @click="channelStore.getList">获取频道数据</button>
<ul>
<li v-for="item in channelStore.getList" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<style scoped></style>
6. storeToRefs工具函数
使用storeToRefs函数可以辅助保持数据(state + getter)的响应式解构
1.定义store
我们知道 Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字
import { defineStore } from 'pinia'
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useCounterStore = defineStore('counter', {
// 其他配置...
})
这个名字 ,也被用作 id
,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use… 是一个符合组合式函数风格的约定。
defineStore() 的第二个参数可接受两类值:Setup 函数
或 Option 对象
。
Setup函数
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
在 Setup Store 中:
- ref() 就是 state 属性
- computed() 就是 getters
- function() 就是 actions
2.使用store
虽然我们前面定义了一个 store,但在我们使用 <script setup> 调用 useStore()(或者使用 setup() 函数,像所有的组件那样) 之前,store 实例是不会被创建的
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>
请注意,store 是一个用 reactive 包装的对象,这意味着不需要在 getters 后面写 .value,就像 setup 中的 props 一样,如果你写了,我们也不能解构它:
<script setup>
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性
// 这就和直接解构 `props` 一样
const { name, doubleCount } = store
name // 将始终是 "Eduardo"
doubleCount // 将始终是 0
// ✅ 这样写是响应式的
// 💡 当然你也可以直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
</script>
为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()
。它将为每一个响应式属性创建引用。当你只使用 store 的状态而不调用任何 action 时,它会非常有用。请注意,你可以直接从 store 中解构 action,因为它们也被绑定到 store 上:
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { name, doubleCount } = storeToRefs(store)
// 作为 action 的 increment 可以直接解构
const { increment } = store
</script>
7. Pinia的调试
Vue官方的 dev-tools 调试工具 对 Pinia直接支持,可以直接进行调试
8. Pinia持久化插件
官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
-
安装插件 pinia-plugin-persistedstate
npm i pinia-plugin-persistedstate
-
使用 main.js
import { createApp } from 'vue' import { createPinia } from 'pinia' import persist from 'pinia-plugin-persistedstate' import App from './App.vue' const pinia = createPinia() ... createApp(App).use(pinia.use(persist))
-
配置 store/counter.js
import { defineStore } from 'pinia' import { computed, ref } from 'vue' export const useCounterStore = defineStore('counter', () => { ... return { count, doubleCount, increment } }, { persist: true })
-
其他配置,看官网文档即可