Vue3学习 pinia
- pinia
- 一个简单效果
- 搭建 pinia 环境
- 存储+读取数据
- 示例
- 修改数据 (三种方式)
- storeToRefs
- getters
- $subscribe
- store组合式写法
pinia
在vue2中使用vuex,在vue3中使用pinia。
集中式状态管理,(状态=数据)。多个组件共享数据的时候使用。
一个简单效果
- Count.vue
注意事项①select中的数字限制 v-model.number ② ref响应式数据 要加value
<template>
<div class="count">
<h1>当前求和为:{{ sum }}</h1>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="add">加</button>
<button @click="minus">减</button>
</div>
</template>
<script setup lang="ts" name="Count">
import {ref} from 'vue'
let sum = ref(0)
let n = ref(1) // 用户选择的数字
// 方法
function add(){
sum.value += n.value
}
function minus(){
sum.value -= n.value
}
</script>
<style>
.count {
background-color: skyblue;
padding: 10px;
padding: 10px;
border-radius: 10px;
}
select,button {
margin-left: 10px;
}
</style>
- LoveTalk.vue
注意事项 ① axios的 async await写法,② reactive监测数组的变化(unshift),③ 连续解构赋值+重命名
<template>
<div class="talk">
<button @click="getLoveTalk">获取一句土味情话</button>
<ul>
<li v-for="talk in talkList" :key="talk.id">{{talk.title}}</li>
</ul>
</div>
</template>
<script setup lang="ts" name="LoveTalk">
import {reactive} from 'vue'
import axios from "axios";
import {nanoid} from 'nanoid'
// 数据
let talkList = reactive([
{id:'ftrfasdf01',title:'今天你有点怪,哪里怪?怪好看的!'},
{id:'ftrfasdf02',title:'草莓、蓝莓、蔓越莓,今天想我了没?'},
{id:'ftrfasdf03',title:'心里给你留了一块地,我的死心塌地'}
])
// 方法
async function getLoveTalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
}
</style>
- App.vue
<template>
<Count></Count>
<hr>
<LoveTalk></LoveTalk>
</template>
<script lang="ts" setup name="App">
import Count from './components/Count.vue';
import LoveTalk from './components/LoveTalk.vue';
</script>
搭建 pinia 环境
第一步:npm install pinia
第二步:操作src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
/* 引入createPinia,用于创建pinia */
import { createPinia } from 'pinia'
/* 创建pinia */
const pinia = createPinia()
const app = createApp(App)
/* 使用插件 */
app.use(pinia)
app.mount('#app')
此时开发者工具中已经有了pinia
选项
存储+读取数据
-
Store
是一个保存:状态、业务逻辑 的实体,每个组件都可以读取、写入它。(/src/store/xxx.ts) -
它有三个概念:
state
、getter
、action
,相当于组件中的:data
、computed
和methods
。 -
具体编码:
src/store/count.ts
// 引入defineStore用于创建store import {defineStore} from 'pinia' // 定义并暴露一个store export const useCountStore = defineStore('count',{ // 动作 actions:{}, // 状态 state(){ return { sum:6 } }, // 计算 getters:{} })
-
具体编码:
src/store/talk.ts
// 引入defineStore用于创建store import {defineStore} from 'pinia' // 定义并暴露一个store export const useTalkStore = defineStore('talk',{ // 动作 actions:{}, // 状态 state(){ return { talkList:[ {id:'yuysada01',content:'你今天有点怪,哪里怪?怪好看的!'}, {id:'yuysada02',content:'草莓、蓝莓、蔓越莓,你想我了没?'}, {id:'yuysada03',content:'心里给你留了一块地,我的死心塌地'} ] } }, // 计算 getters:{} })
-
组件中使用
state
中的数据<template> <h2>当前求和为:{{ sumStore.sum }}</h2> </template> <script setup lang="ts" name="Count"> // 引入对应的useXxxxxStore import {useSumStore} from '@/store/sum' // 调用useXxxxxStore得到对应的store const sumStore = useSumStore() </script>
<template> <ul> <li v-for="talk in talkStore.talkList" :key="talk.id"> {{ talk.content }} </li> </ul> </template> <script setup lang="ts" name="Count"> import axios from 'axios' import {useTalkStore} from '@/store/talk' const talkStore = useTalkStore() </script>
示例
我想让 count.vue中的sum变量、Lovetalk.vue中的talkList变成共有的
① npm i pinia
② 创建store文件夹,store可以理解为 使用pinia的实体。里面对应组件创建ts文件
count.ts
import {defineStore} from 'pinia'
// useCountStore和hooks命名方法一致
export const useCountStore = defineStore('count', {
// 真正存储数据的地方
state(){ 状态(数据)
return {
sum :6
}
}
})
loveTalk.ts
import {defineStore} from 'pinia'
// useCountStore和hooks命名方法一致
export const useTalkStore = defineStore('talk', {
// 真正存储数据的地方
state(){ 状态(数据)
return {
talkList: [
{id:'ftrfasdf01',title:'今天你有点怪,哪里怪?怪好看的!'},
{id:'ftrfasdf02',title:'草莓、蓝莓、蔓越莓,今天想我了没?'},
{id:'ftrfasdf03',title:'心里给你留了一块地,我的死心塌地'}
]
}
}
})
③ 在组件中使用 store里的数据
count.vue
<template>
<div class="count">
<h1>当前求和为:{{ countStore.sum }}</h1>
</div>
</template>
<script setup lang="ts" name="Count">
// let sum = ref(0)
import { useCountStore } from '@/store/count';
const countStore = useCountStore()
//console.log(countStore);
// 以下两种方式都可以拿到state中的 数据
//console.log(countStore.sum);
//console.log(countStore.$state.sum);
</script>
拿到 store 中的数据:
import { useCountStore } from '@/store/count';
const countStore = useCountStore()
打印countStore:
修改数据 (三种方式)
import { useCountStore } from '@/store/count';
const countStore = useCountStore()
-
第一种修改方式,直接修改
countStore.sum = 666
-
第二种修改方式:批量修改
countStore.$patch({ sum:999, school:'atguigu' })
-
第三种修改方式:借助
action
修改(action
中可以编写一些业务逻辑)底层维护了一个this。
import { defineStore } from 'pinia' export const useCountStore = defineStore('count', { /*************/ actions: { //加 increment(value:number) { if (this.sum < 10) { //操作countStore中的sum this.sum += value } }, //减 decrement(value:number){ if(this.sum > 1){ this.sum -= value } } }, /*************/ })
组件中调用
action
即可// 使用countStore const countStore = useCountStore() // 调用对应action countStore.incrementOdd(n.value)
storeToRefs
借助storeToRefs
将store
中的数据转为ref
对象,方便在模板中使用。
注意:pinia
提供的storeToRefs
只会将数据做转换,不会对方法进行包裹。而Vue
的toRefs
会转换store
中数据。
<template>
<div class="count">
<h2>当前求和为:{{sum}}</h2>
</div>
</template>
<script setup lang="ts" name="Count">
import { useCountStore } from '@/store/count'
/* 引入storeToRefs */
import { storeToRefs } from 'pinia'
/* 得到countStore */
const countStore = useCountStore()
/* 使用storeToRefs转换countStore,随后解构 */
const {sum} = storeToRefs(countStore)
</script>
getters
-
概念:当
state
中的数据,需要经过处理后再使用时,可以使用getters
配置。类似computedgetters 中维护的this也是store
-
追加
getters
配置。// 引入defineStore用于创建store import {defineStore} from 'pinia' // 定义并暴露一个store export const useCountStore = defineStore('count',{ // 动作 actions:{ /************/ }, // 状态 state(){ return { sum:1, school:'atguigu' } }, // 计算 getters:{ bigSum: state => state.sum *10, upperSchool():string{ return this.school.toUpperCase() } } })
-
组件中读取数据:
const {increment,decrement} = countStore let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)
$subscribe
通过 store 的 $subscribe()
方法侦听 state
及其变化
- LoveTalk.vue
import { useTalkStore } from '@/store/count';
const talkList = useTalkStore()
talkStore.$subscribe((mutate,state)=>{
// console.log('talkStore发生变化');
console.log('LoveTalk',mutate,state)
localStorage.setItem('talk',JSON.stringify(talkList.value))
})
- loveTalk.ts
export const useTalkStore = defineStore('talk', {
state(){
return {
// 断言+空数组
talkList: JSON.parse(localStorage.getItem('talk') as string)||[]
}
}
...
})
store组合式写法
- 之前是选项式
import {defineStore} from 'pinia'
import axios from "axios";
import {nanoid} from 'nanoid'
// defineStore('名字',{配置对象})
export const useTalkStore = defineStore('talk', {
state(){
return {
talkList: JSON.parse(localStorage.getItem('talk') as string)||[]
}
},
actions: {
async getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
this.talkList.unshift(obj)
}
},
getters:{
}
})
-
组合式写法(注意defineStore的第二项变为函数,需要返回值)
① state数据直接用 reactive/ref
② actions里面的函数配置,写为function函数
③ 没有this了
import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk函数相当于action
async function getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
return {talkList,getATalk}
})
- count.ts
import {ref} from 'vue'
export const useCountStore = defineStore('count',()=>{
let sum = ref(6)
function increment(value:number) {
if (sum.value < 10) {
//操作countStore中的sum
sum.value += value
}
}
//减
function decrement(value:number){
if(sum.value > 1){
sum.value -= value
}
}
return {sum,increment,decrement}
})