状态管理:
集中式状态(数据)管理 例如 redux vuex pinia
搭建pinia环境:
1.终端下载pinia --> npm i pinia
2.在main.ts文件中引入、创建、安装pinia
import { createApp } from 'vue'
import App from './App.vue'
// 第一步:引入pinia
import { createPinia } from 'pinia'
const app = createApp(App)
//第二步:创建pinia
const pinia = createPinia()
//第三步:安装pinia
app.use(pinia)
app.mount('#app')
使用pinia:
存储数据:
src下创建store文件夹,我们创建两个ts文件,用来存储数据
count.ts文件存储求和的数据:
import {defineStore} from "pinia"
export const useCountStore = defineStore('count',{
state(){
return{
sum:6
}
}
})
loveTalk.ts 存储土味情话的数组
import { defineStore } from "pinia"
export const useTalkStore = defineStore('talk', {
state() {
return {
talkList: [
{ id: 'cankm01', title: '今天你有点怪,哪里怪?怪好看的!' },
{ id: 'cankm02', title: '草莓、蓝莓、蔓越莓,你今天想我了没' },
{ id: 'cankm03', title: '心里给你留了一块地,我的死心塌地' },
]
}
}
})
以上就是我们需要在pinia里面存储的数据啦,将他们分别暴露出来,下面我们来说如何在vue文件里面读取数据
读取数据:
我们在Count.vue里面读取count.ts里面的sum数据:
第一步:引入useCountStore
import { useCountStore } from '@/store/count'
第二步:拿到我们需要的数据:
const conutStore = useCountStore()
//conutStore是一个reactive对象,里面有一个sum是ref对象
// 以下两种方式都可以拿到state中的数据
// console.log(conutStore.sum);
// console.log(conutStore.$state.sum);
问:为什么打印出来的conutStore.sum分明是ref包裹的响应式对象数据,为什么不要conutStore.sum.value来获取呢?
// 举例
let obj = reactive({
a:1,
b:2,
c:ref(3)
})
console.log(obj.a);
console.log(obj.b);
//不需要写obj.c.value;因为外层包裹reactive已经默认解包了,直接用即可
console.log(obj.c);
let x =ref(9)
console.log(x.value);
完整代码:
<template>
<div class="count">
<h2>当前求和为:{{ conutStore.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="add">加</button>
<button @click="minus">减</button>
</div>
</template>
<script lang="ts" setup name="Count">
import { ref, reactive } from 'vue';
import { useCountStore } from '@/store/count'
// conutStore是一个reactive对象,里面有一个sum是ref对象
// 所以我们需要的sum是conutStore.sum即可,不需要conutStore.sum.value【下面有举例⬇】
const conutStore = useCountStore()
// 以下两种方式都可以拿到state中的数据
// console.log(conutStore.sum);
// console.log(conutStore.$state.sum);
// 举例
/** let obj = reactive({
a:1,
b:2,
c:ref(3)
})
console.log(obj.a);
console.log(obj.b);
//不需要写obj.c.value;因为外层包裹reactive已经默认解包了,直接用即可
console.log(obj.c);
let x =ref(9)
console.log(x.value);*/
// 数据
let n = ref(1) //用户选择的数据
// 方法
function add() {
// sum.value += n.value
}
function minus() {
// sum.value -= n.value
}
</script>
<style scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,
button {
margin: 0 5px;
height: 25px;
}
</style>
同理,我们获取loveTalk.ts里面的数据在LoveTalk.vue里面展示:
<template>
<div class="talk">
<button @click="getLoveTalk">获取一句土味情话</button>
<ul>
<li v-for="talk in talkStore.talkList" :key="talk.id">{{ talk.title }}</li>
</ul>
</div>
</template>
<script lang="ts" setup name="LoveTalk">
import { reactive } from 'vue';
import axios from "axios";
import { nanoid } from "nanoid";
import {useTalkStore} from '@/store/loveTalk'
const talkStore = useTalkStore()
// 方法
async function getLoveTalk() {
// // 发请求
// // 扩展【也可以写 let {data:{content}} 进一步解构 直接使用content 】
// // 【let {data:{content:titel}} 进一步解构处content之后将它重命名为title】【即连续解构赋值+重命名】
// let {data} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// // 把请求回来的字符串,包装成一个对象
// let obj = { id: nanoid(), title: data.content }
// // 放到数组中 在数组的开头添加
// talkList.unshift(obj)
}
</script>
<style lang="less" scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
修改数据的三种方式:
第一种方式:直接用countStore拿到我们需要的sum数据去修改
// 方法
function add() {
// 第一种修改方式
countStore.sum += 1
}
第二种方式:更适合用于批量修改数据,只用$patch批量修改一次
// 方法
function add() {
// 第二种修改方式【批量修改数据,只用$patch批量修改一次】
countStore.$patch({
sum:888,
school:'复旦大学',
address:'上海'
})
}
第三种修改方式:在count.ts里面修改数据,其中actions调用state里面的数据不能直接用state,而是用this,这里的this就是当前的store
import {defineStore} from "pinia"
export const useCountStore = defineStore('count',{
// actions里面放置的是一个一个的方法,用于相应组件中的“动作”
actions:{
increment(value:number){
// this是当前的store【CountStore】,而不能直接state调用
this.sum += value
}
},
// 真正存储数据的地方
state(){
return{
sum:6,
school:'xx学校',
address:"北京市"
}
}
})
在vue的方法里调用它:【在处理比较复杂的逻辑的时候可以使用第三种】
// 方法
function add() {
// 第三种修改方式
countStore.increment(n.value)
}
对于我们的土味情话的例子,LoveTalk.vue和loveTalk.ts同理,这里使用了第三种方法
import { defineStore } from "pinia"
import axios from "axios";
import { nanoid } from "nanoid";
export const useTalkStore = defineStore('talk', {
actions: {
async getATalk() {
// 发请求
// 扩展【也可以写 let {data:{content}} 进一步解构 直接使用content 】
// 【let {data:{content:titel}} 进一步解构处content之后将它重命名为title】【即连续解构赋值+重命名】
let { data } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = { id: nanoid(), title: data.content }
// 放到数组中 在数组的开头添加
this.talkList.unshift(obj)
}
},
state() {
return {
talkList: [
{ id: 'cankm01', title: '今天你有点怪,哪里怪?怪好看的!' },
{ id: 'cankm02', title: '草莓、蓝莓、蔓越莓,你今天想我了没' },
{ id: 'cankm03', title: '心里给你留了一块地,我的死心塌地' },
]
}
}
})
<template>
<div class="talk">
<button @click="getLoveTalk">获取一句土味情话</button>
<ul>
<li v-for="talk in talkStore.talkList" :key="talk.id">{{ talk.title }}</li>
</ul>
</div>
</template>
<script lang="ts" setup name="LoveTalk">
import { useTalkStore } from '@/store/loveTalk'
const talkStore = useTalkStore()
// 方法
function getLoveTalk() {
talkStore.getATalk()
}
</script>
<style lang="less" scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
storeToRefs:
为什么不直接用toRefs包裹呢?
因为如果用toRefs包括将会把countStore所有的数据都用ref包裹, 而用storeToRefs指挥关注store的数据,不会对方法进行ref包裹
<template>
<div class="count">
<h2>当前求和为:{{ sum }}</h2>
<h3>欢迎来到:{{ school }},坐落于:{{ address }}</h3>
<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 lang="ts" setup name="Count">
import { ref } from 'vue';
import { storeToRefs } from "pinia";
// 引入useCountStore
import { useCountStore } from '@/store/count'
//使用useCountStore,得到一个专门保存count相关的store
const countStore = useCountStore()
// 为什么不直接用toRefs包裹呢?
// 因为如果用toRefs包括将会把countStore所有的数据都用ref包裹,
// 而用storeToRefs指挥关注store的数据,不会对方法进行ref包裹
const { sum, school, address } = storeToRefs(countStore)
// 数据
let n = ref(1) //用户选择的数据
// 方法
function add() {
countStore.increment(n.value)
}
function minus() {
countStore.sum -= n.value
}
</script>
<style scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,
button {
margin: 0 5px;
height: 25px;
}
</style>
getters的使用:
this是当前的store!!!!在下面的例子里是【CountStore】!
import { defineStore } from "pinia"
interface CountState {
sum: number;
school: string;
address: string;
}
export const useCountStore = defineStore('count', {
// actions里面放置的是一个一个的方法,用于相应组件中的“动作”
actions: {
increment(value: number) {
// this是当前的store【CountStore】,而不能直接state调用
this.sum += value
}
},
// 真正存储数据的地方
state: (): CountState => ({
sum: 1,
school: 'abcd',
address: "北京市"
}),
getters: {
bigSum(state): number {
return state.sum * 10
},
// 简写
// bigSum: state => state.sum * 10,
upperSchool(state): string {
return state.school.toUpperCase()
},
// upperSchool():string {
// // 可以直接用this,如果用this不能用箭头函数
// return this.school.toUpperCase()
// }
}
})
如果在vue里面使用,则直接同state里面的数据一样解析出来;如下代码:
<template>
<div class="count">
<h2>当前求和为:{{ sum }},放大十倍后:{{ bigSum }}</h2>
<h3>欢迎来到:{{ school }},坐落于:{{ address }},大写:{{ upperSchool }}</h3>
<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 lang="ts" setup name="Count">
import { ref } from 'vue';
import { storeToRefs } from "pinia";
// 引入useCountStore
import { useCountStore } from '@/store/count'
//使用useCountStore,得到一个专门保存count相关的store
const countStore = useCountStore()
// 为什么不直接用toRefs包裹呢?
// 因为如果用toRefs包括将会把countStore所有的数据都用ref包裹,
// 而用storeToRefs指挥关注store的数据,不会对方法进行ref包裹
const { sum, school, address, bigSum, upperSchool } = storeToRefs(countStore)
// 数据
let n = ref(1) //用户选择的数据
// 方法
function add() {
countStore.increment(n.value)
}
function minus() {
countStore.sum -= n.value
}
</script>
<style scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,
button {
margin: 0 5px;
height: 25px;
}
</style>
$subscribe的使用:
有store我们就可以使用$subscribe【有种watch的感觉】订阅状态及其变化,可以监测到store中数据的变化;两个参数–> mutate:本次修改信息 state:真正信息
talkStore.$subscribe((mutate,state)=>{
//localStorage存储的话必须存储字符串形式
localStorage.setItem('talkList',JSON.stringify(state.talkList))
})
LoveTalk.vue完整代码:
<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 lang="ts" setup name="LoveTalk">
import { useTalkStore } from '@/store/loveTalk'
import { storeToRefs } from "pinia";
const talkStore = useTalkStore()
const {talkList} = storeToRefs(talkStore)
// 订阅状态及其变化,可以监测到store中数据的变化
// 两个参数--> mutate:本次修改信息 state:真正信息
talkStore.$subscribe((mutate,state)=>{
//localStorage存储的话必须存储字符串形式
localStorage.setItem('talkList',JSON.stringify(state.talkList))
})
// 方法
function getLoveTalk() {
talkStore.getATalk()
}
</script>
<style lang="less" scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
我们在store里面loveTalk.ts:
在state里面我们也可以将其本地获取,刷新不会丢失:
talkList: JSON.parse(localStorage.getItem('talkList') as string) || []
loveTalk.ts完整代码:
import { defineStore } from "pinia"
import axios from "axios";
import { nanoid } from "nanoid";
export const useTalkStore = defineStore('talk', {
actions: {
async getATalk() {
// 发请求
// 扩展【也可以写 let {data:{content}} 进一步解构 直接使用content 】
// 【let {data:{content:titel}} 进一步解构处content之后将它重命名为title】【即连续解构赋值+重命名】
let { data } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = { id: nanoid(), title: data.content }
// 放到数组中 在数组的开头添加
this.talkList.unshift(obj)
}
},
state() {
return {
// 本地存储后刷新数据不会丢失
talkList: JSON.parse(localStorage.getItem('talkList') as string) || []
}
}
})
store的组合式写法:
如果选择组合式写法一定要 return{ } 出去!!
import { defineStore } from "pinia"
import axios from "axios";
import { nanoid } from "nanoid";
// 选项式写法
/**export const useTalkStore = defineStore('talk', {
actions: {
async getATalk() {
let { data } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
let obj = { id: nanoid(), title: data.content }
this.talkList.unshift(obj)
}
},
state() {
return {
// 本地存储后刷新数据不会丢失
talkList: JSON.parse(localStorage.getItem('talkList') as string) || []
}
}
})**/
// 组合式写法
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 } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
let obj = { id: nanoid(), title: data.content }
talkList.unshift(obj)
}
return { talkList, getATalk }
})
1054

被折叠的 条评论
为什么被折叠?



