前言:
pinia是vue3发布之后对vuex的一个升级版本,它们有一些相似之处,总之非常好用,接下来我就来一步一步创建我的pinia仓库并比较它与vuex有哪些不同。
实现步骤
1.下载依赖
npm:
npm install pinia
pnpm:
pnpm install pinia
yarn:
yarn add pinia
建议使用pnpm或yarn下载依赖,因为npm会产生一些幽灵依赖。
2.在main.js中使用
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
3.创建仓库文件(Options API风格)
一般情况下,会在src目录下创建一个stores文件夹,在该文件夹中去创建状态管理仓库。代码如下:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('num', {
state: () => {
return {}
},
getters: {},
actions: {}
})
现在就创建好了一个基本的状态管理创库了,首先直观的可以观察到与vux不同的是pinia没有modules,只有state、getters、actions。
其次在vuex中state是一个对象,而pinia则是一个函数。
4.state
export const useCounterStore = defineStore('user', {
state: () => {
return {
userName: '张三',
age: 18
}
},
getters: {},
actions: {}
})
不过,不管是vuex还是pinia,定义的数据都放在state中,我这边就随便定义了两个数据。接下来我们就要在组件中去使用我们定义的数组
1.在组件中使用
<template>
<div class="app">
<div>name : {{ user.userName }}</div>
<div>age : {{ user.age }}</div>
<button @click="handleAge">点击</button>
</div>
</template>
<script setup>
import { useCounterStore } from './stores/user'
const user = useCounterStore()
const handleAge = () => {
user.age = 20;
}
</script>
以上就将创建好的状态仓库导入到了组件中,我们也可以通过方法直接改变定义数据的值,而vuex需要通过mutations进行提交。
也可以使用解构的方式获取到状态仓库中的数据。
<template>
<div class="app">
<div>name : {{ userName }}</div>
<div>age : {{ age }}</div>
<button @click="handleAge">点击</button>
</div>
</template>
<script setup>
import { useCounterStore } from './stores/user'
const store = useCounterStore()
const { userName, age } = store
const handleAge = () => {
age = 20;
}
</script>
但解构之后,发现点击按钮之后会报错,无法修改数据了,这是因为当我们解构之后这个数据不是响应式数据,因此当我们解构的时候需要做另外的操作,使用到了storeToRefs API。
<template>
<div class="app">
<div>name : {{ userName }}</div>
<div>age : {{ age }}</div>
<button @click="handleAge">点击</button>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from './stores/user'
const store = useCounterStore()
const { userName, age } = storeToRefs(store)
const handleAge = () => {
age.value = 20;
}
</script>
这样解构之后的数据依然是响应式数据。
2.批量更新
当我们需要对多个数据进行批量操作时,代码如下
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from './stores/user'
const store = useCounterStore()
const { userName, age } = storeToRefs(store)
const handleAge = () => {
// 批量操作
// 第一种方式:
// userName.value = '李四'
// age.value++
// 第二种方式:
store.$patch(state => {
state.userName = '李四'
state.age++
})
}
</script>
3.总结:
来总结一下state在pinia和vuex中的不同:
1.写法上:pinia中state是一个函数,而vuex中state是一个对象;
2.修改数据:pinia中可以直接修改state的数据,而vuex中不可以直接修改state中的数据,需要通过mutation提交才可以。
5.getters
getters作为一个计算属性,并且有缓存机制,与vuex几乎一样。
export const useCounterStore = defineStore('user', {
state: () => {
return {
userName: '张三',
age: 18
}
},
getters: {
setAge(){
// 打印 缓存机制
console.log('getters');
return this.age + 100;
}
},
actions: {}
})
我在getters中写了一个计算属性setAge,它做的事就是将age加100,并且我打印了一条数据,是为了证明它有缓存。
<template>
<div class="app">
<div>name : {{ userName }}</div>
<div>age : {{ age }}</div>
<div>{{ setAge }}</div>
<div>{{ setAge }}</div>
<div>{{ setAge }}</div>
<div>{{ setAge }}</div>
<button @click="handleAge">点击</button>
</div>
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useCounterStore } from './stores/user'
const store = useCounterStore()
const { userName, age, setAge } = storeToRefs(store)
const handleAge = () => {
// 批量操作
// 第一种方式:
// userName.value = '李四'
// age.value++
// 第二种方式:
store.$patch(state => {
state.userName = '李四'
state.age++
})
}
</script>
这边我将setAge渲染了多次 ,但控制台只打印了一次,因为getters是有缓存机制的。
6.actions
actions是用来写方法的,它支持同步以及异步。
actions: {
getAge(val) {
this.age += val
}
}
定义一个方法,这个方法接收一个参数,执行一次将age增加一次对应参数的值,在组件中使用这个方法。
<button @click="handleAdd">增加</button>
const handleAdd = () => {
store.getAge(2)
}
新增加一个按钮,给按钮绑定点击事件,点击一次就执行一次actions中的getAge。
以上基本的一个实现思路就完成了,不过创建仓库还有第二种写法。
第二种创建状态仓库的方式(Composition API风格)
在上一种方式中,defineStore的第二个参数是数组,它也可以是一个函数,当第二个参数是一个函数时,写法就发生了区别,以下我将上面演示的小例子改为第二种写法。代码如下:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('num', () => {
const userName = ref('张三')
const age = ref(18)
const setAge = computed(() => age.value + 100)
function getAge(val) {
age.value += val
}
return { userName, age, setAge, getAge }
})
这种方式更符合vue3的书写习惯,说的通俗易懂就是ref相当于state,computed相当于getters,function相当于action。
这两种的效果是一样的,具体使用哪种看你的书写习惯。
结语:
Pinia作为Vue3的官方推荐状态管理库,以其简洁的API设计和更好的TypeScript支持,显著提升了开发体验。通过对比Vuex,Pinia的优势主要体现在以下方面:
Pinia取消了Vuex中繁琐的modules
概念,简化了状态组织的复杂度。其state
采用函数式返回,更契合Composition API的设计理念,而Vuex的state
是直接定义的对象结构。
Pinia允许直接修改state
数据,无需通过mutations
提交,大幅减少模板代码。getters
的缓存机制与Vuex一致,但通过Composition API的computed
实现更直观。actions
同时支持同步和异步操作,与组件逻辑无缝衔接。
两种仓库创建方式(Options API风格与Composition API风格)为开发者提供了灵活性,后者尤其适合Vue3的响应式编程范式。storeToRefs
API解决了响应式数据解构的痛点,体现了Pinia对开发者体验的深度优化。
总体而言,Pinia在保留状态管理核心功能的同时,通过更现代的设计降低了学习成本,是Vue3项目状态管理的理想选择。