pinia
# Pinia简介
Pinia 是 Vue 的存储库,允许您跨组件/页面共享状态。Pinia 这款产品最初是为了探索 Vuex 的下一个版本, 整合了核心团队关于 Vuex 5 的许多想法。最终,我们意识到 Pinia 已经实现了我们想要在 Vuex 5 中提供的大部分内容, 因此决定将其作为 新的官方推荐。
官网:https://pinia.vuejs.org/zh/ (opens new window)
GitHub: https://github.com/vuejs/pinia (opens new window)
# Pinia特点
- 足够轻量,Pinia 重约 1kb,甚至会忘记它的存在!
- 去除 Mutation ,Actions 支持同步和异步(Actions一个顶俩,写起来简洁);
- 无需手动注册 Store,Store 仅需要时才自动注册。如果从不使用,则永远不会“注册”(省心);
- 没有模块嵌套,只有 Store 的概念,Store 之间可以自由使用,更好的代码分割;
- Vue2 和 Vue3 都能支持;
- 支持大型项目迁移期间,Pinia 和 Vuex 混合使用(贴心迁移);
- 更完美的 typescript 的支持;
- 与 Vue devtools 挂钩,Vue2 和 Vue3 开发体验更好;
- 支持插件扩展功能;
- 支持模块热更新,无需加载页面可以修改容器,可以保持任何现有的状态;
- 支持服务端渲染
# Pinia使用
# 1、安装
npm install pinia
注意:vue2使用的话,需要另外安装
npm i pinia @vue/composition-api --save
# 2、定义 Store
新建 src/stores 目录并在其下面创建 index.ts
Pinia 的目录通常被称为 stores 而不是 store, 这是为了强调 Pinia 使用多个 store,而不是 Vuex 中的单个 store,同时也有迁移期间 Pinia 和 Vuex 混合使用的考虑。
// src/stores/index.ts
// 引入Store定义函数
import { defineStore } from 'pinia'
// 定义Store实例并导出,useStore可以是任何东西,比如useUser, useCart
// 第一个参数,唯一不可重复,字符串类型,作为仓库ID 以区分仓库
// 第二个参数,以对象形式配置仓库的state,getters,actions
export const useStore = defineStore('main', {
// state 推荐箭头函数,为了TS类型推断
state: () => {
return {
name: '张三',
counter: 0
}
},
getters: {},
actions: {}
})
# 3、在 main.ts 中引入并挂载到根实例
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 创建Vue应用实例
// 实例化 Pinia
// 以插件形式挂载Pinia实例
createApp(App).use(createPinia()).mount('#app')
# 4、访问State
<template>
<div>
<button @click="handleClick">修改状态数据</button>
<!-- 模板内不需要加.value -->
<p>{{store.name}}</p>
<!-- computed获取 -->
<p>{{name}}</p>
<!-- 或者使用解构之后的 -->
<p>{{counter}}</p>
</div>
</template>
<script lang="ts" setup>
import { useStore } from '@/stores/index.ts'
// 使普通数据变响应式的函数
import { storeToRefs } from "pinia";
const store = useStore()
// 结合computed获取
const name = computed(() => store.name)
// 解构并使数据具有响应式
const { counter } = storeToRefs(store);
// 点击 + 1;
function handleClick() {
// ref数据这里需要加.value访问
counter.value++;
}
</script>
# 5、修改State
单个参数修改 state
store.counter++
多个参数修改 state
store.$patch({
counter: store.counter + 1,
name: 'test1',
})
- 全部修改 state
store.$state = { counter: 666, name: 'test2' }
或
pinia.state.value = {}
# 6、重置State
将状态重置为初始值
const store = useStore()
store.$reset()
# 7、Getters
- 定义Getters
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
// 自动推导返回类型
doubleCount(state) {
return state.counter * 2
},
// 依赖getters返回参数,则需要显性的设置返回类型
doublePlusOne(): number {
return this.doubleCount + 1
},
},
})
- 使用Getters
<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
<script>
export default {
setup() {
const store = useStore()
return { store }
},
}
</script>
# 8、Actions
注意:对比vuex,Pinia 中删除了 Mutation,Actions 支持同步和异步
(1)定义 Actions
// 同步
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
userData: null,
}),
actions: {
increment() {
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})
// 异步
import { mande } from 'mande'
const api = mande('/api/users')
export const useUsers = defineStore('users', {
state: () => ({
userData: null,
}),
actions: {
async registerUser(login, password) {
this.userData = await api.post({ login, password })
},
},
})
(2)调用 Actions
- 直接调用 store
<script>
export default {
setup() {
const store = useStore()
store.randomizeCounter()
},
}
</script>
- action 间的相互调用,直接用 this 访问
export const useUserStore = defineStore({'user',
actions: {
increment() {
this.counter++
},
increase() {
// 调用另一个 action 的方法
this.increment()
},
}
})
- 在 action 里调用其他 store 里的 action
import { useAuthStore } from './auth-store'
export const useSettingsStore = defineStore('settings', {
state: () => ({
preferences: null,
}),
actions: {
async fetchUserPreferences() {
// 调用 auth-store store 里的 action 方法
const auth = useAuthStore()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
},
})