2025-6-9Vue3快速上手
1、搭建pinia环境
开始 | Pinia
npm i pinia
在main.ts配置
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
//引入Pinia
import { createPinia } from 'pinia'
const app = createApp(App)
//创建Pinia
const pinia = createPinia()
//安装Pinia
app.use(pinia)
app.use(router)
app.mount("#app")
2、存储+读取数据
在src下新建一个store文件夹,文件夹中新建count.ts文件,用于管理count相关数据/状态
import {defineStore} from 'pinia'export const useCountStore = defineStore('count',{//真正存储数据的地方state(){return {sum:1}}
})
在Count.vue读取数据
state数据是reactive包裹的对象,state中的sum是ref对象,当获取reactive对象中的ref对象时,不需要拆包(即不需要加.value)
let obj = reactive({a:'%%',b:'8*',c:ref(7)
})
console.log(obj.a)
console.log(obj.b)
console.log(obj.c)
在 Pinia 中,state
默认是通过 Vue 的 reactive
包裹的对象,因此:
- 整个
state
是一个reactive
对象。 - 如果
state
中的某个属性(如sum
)是基本类型(如number
、string
),Pinia 会自动将其转换为ref
对象(底层实现)。 - 但你在使用时,不需要手动
.value
拆包,因为 Pinia 做了特殊处理。
<template><h2>当前求和为{{ countStore.sum }}</h2><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="addSum">加</button><button @click="reduceSum">减</button>
</template><script lang="ts" setup name="Count">
import {ref} from 'vue'
import { useCountStore } from '../store/count';
let countStore = useCountStore();
console.log('@@',countStore)
//以下两种方式都能获取state中的数据
console.log(countStore.sum)
console.log(countStore.$state.sum)let n= ref(1);
function addSum(){countStore.sum +=n.value;
}
function reduceSum(){countStore.sum -=n.value
}
</script><style scoped></style>
3、修改数据(三种方式)
(1)直接修改:
countStore.sum +=n.value;
(2)批量修改:
countStore.$patch({sum:888,school:'农',address:'天河'})
(3)借助action修改,action中可以写一些业务逻辑(方便代码复用)
countStore.increment(n.value)
function addSum(){//第一种修改方式:// countStore.sum +=n.value;// countStore.school = 'scau';// countStore.address = 'wushan';//第二种修改方式:// countStore.$patch({// sum:888,// school:'农',// address:'天河'// })//第三种修改方式:countStore.increment(n.value)
}
import {defineStore} from 'pinia'export const useCountStore = defineStore('count',{//actions中放置一个个的方法,用于相应组件中的“动作”actions:{increment(value:number){//this 指向的是 当前 Store 的实例(即 useCountStore)this.sum += value;}},//真正存储数据的地方state(){return {sum:1,school:'华农',address:'五山'}}
})
4、storeToRefs
storeToRefs
(Pinia 专用)
作用:
专门用于解构 Pinia Store,仅提取 state
中的响应式数据(不包括 getters
和 actions
),并将这些数据转换为 ref
对象,以保持响应性。
特点:
- 只关注
state
:忽略getters
和actions
。 - 转换为
ref
:解构后的每个属性都是ref
对象,需通过.value
访问(但在模板中自动解包,无需手动.value
)。
toRefs
(Vue 原生 API)
作用:
将一个响应式对象(如 reactive
创建的对象)转换为普通对象,其中每个属性都是 ref
对象,以保持解构后的响应性。
特点:
- 作用于任何响应式对象:不仅限于 Pinia Store,也可以是普通的
reactive
对象。 - 转换为
ref
:解构后的每个属性都是ref
对象,需通过.value
访问(模板中自动解包)。 - 不区分
state
/getters
/actions
:会解构所有可枚举属性(包括方法,但方法会被错误地转为ref
,导致调用失效)。
storeToRefs将store中的数据解构出来,在模版中使用数据会更加优雅
<template><h2>当前求和为{{ sum }}</h2><h2>学校:{{ school }} 坐落于:{{ address }} </h2><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="addSum">加</button><button @click="reduceSum">减</button>
</template><script lang="ts" setup name="Count">
import {reactive, ref, toRefs} from 'vue'
import { useCountStore } from '../store/count';
import { storeToRefs } from 'pinia';
let countStore = useCountStore();
const {sum,school,address} = storeToRefs(countStore)
console.log('toRefs',toRefs(countStore))
console.log('storeToRefs',storeToRefs(countStore))let n= ref(1);
function addSum(){countStore.increment(n.value)
}
function reduceSum(){// countStore.sum -=n.valuesum.value -= n.value
}</script><style scoped></style>
5、getters的使用
在 Pinia 中,getters
的作用类似于 Vue 组件中的 computed
属性,主要用于基于 Store 的 state
派生出新的状态(计算属性),并且具有缓存和响应式的特性。它的核心用途是避免重复计算,优化性能,同时保持数据的同步更新。
import {defineStore} from 'pinia'export const useCountStore = defineStore('count',{//actions中放置一个个的方法,用于相应组件中的“动作”actions:{increment(value:number){//this 指向的是 当前 Store 的实例(即 useCountStore)if(this.sum < 10){this.sum += value;}}},//真正存储数据的地方state(){return {sum:1,school:'huanong',address:'五山'}},getters:{bigSum:state=>state.sum * 10,upperSchool():string{return this.school.toUpperCase()}}
})
<template><h2>当前求和为{{ sum }} 放大十倍:{{ bigSum }}</h2><h2>学校:{{ school }} 坐落于:{{ address }} 大写 {{ upperSchool }} </h2><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="addSum">加</button><button @click="reduceSum">减</button>
</template><script lang="ts" setup name="Count">
import {reactive, ref, toRefs} from 'vue'
import { useCountStore } from '../store/count';
import { storeToRefs } from 'pinia';
let countStore = useCountStore();
//storeToRefs只会关注store中的数据,不会对方法进行ref包裹
const {sum,school,address,bigSum,upperSchool} = storeToRefs(countStore)
console.log('toRefs',toRefs(countStore))
console.log('storeToRefs',storeToRefs(countStore))let n= ref(1);
function addSum(){countStore.increment(n.value)
}
function reduceSum(){// countStore.sum -=n.valuesum.value -= n.value
}</script><style scoped></style>
6、$subscribe的使用
$subscribe
是一个重要的方法,用于监听 store 中状态的变化
常用:持久化状态(eg:将数据保存到LocalStorage)
countStore.$subscribe((mutate,state)=>{console.log('countStore的数据发生了变化',mutate,state)localStorage.setItem('count',JSON.stringify(state.sum))
})
对于复杂的持久化需求,推荐使用官方插件 pinia-plugin-persistedstate
7、store的组合式写法
//store的组合式写法
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';export const useCountStore = defineStore('count', () => {// 状态定义const sum = ref(1);const school = ref('huanong');const address = ref('五山');// actions (方法)const increment = (value: number) => {if (sum.value < 10) {sum.value += value;}};// getters (计算属性)const bigSum = computed(() => sum.value * 10);const upperSchool = computed(() => school.value.toUpperCase());// 订阅状态变化const subscribe = (callback: (mutation: any, state: any) => void) => {return useCountStore().$subscribe(callback);};// 导出状态和方法return {sum,school,address,increment,bigSum,upperSchool,subscribe};
});
//store的选项式写法
import {defineStore} from 'pinia'export const useCountStore = defineStore('count',{//actions中放置一个个的方法,用于相应组件中的“动作”actions:{increment(value:number){//this 指向的是 当前 Store 的实例(即 useCountStore)if(this.sum < 10){this.sum += value;}}},//真正存储数据的地方state(){return {sum:1,school:'huanong',address:'五山'}},getters:{bigSum:state=>state.sum * 10,upperSchool():string{return this.school.toUpperCase()}}
})