043_对pinia的理解
pinia 官网
https://pinia.vuejs.org/zh/


集中式状态(数据)管理
react ====> redux
vue2 ====> vuex
vue3 ====> pinia


嵌套组件

集中式状态(数据)管理除redux,vuex,pinia等以外,还包括github.com上的第三方库,其实有很多都能做集中式状态管理,什么叫集中式,简单来说,就是把所有要管理的数据放在一个容器里面,放在一个地方,这就叫集中式状态(数据)管理,跟他相对应的一个概念叫做分布式。
当我们开发的时候,可能有一个根组件叫App,当我们的应用越来越复杂,功能越来越多的时候,就会出现非常多的组件,甚至组件之间还会嵌套,随你编写的应用越来越多,很有可能会出现这个模样,比如1号组件和2号组件要共享一下数据,1号组件为登录组件,用户登录成功之后,会存储用户的信息,而2号组件是属于抽奖页面,比如说今天是双11,有一个双11抽奖页面,用户点击这个组件进去之后,要求就是抽奖页面,要呈现用户的头像,要展示用户的用户名,其实就是一号组件与二号组件要共享一下数据,这时候你再考虑到用 pinia集中式状态管理,老师我们用了集中式,那这样比如3号组件,4号组件都不往他们里面写数据,一切数据都交给 pinia 管理,这是不合理的,官方说了如下一句话:
你要将那些共享的数据交给集中式管理,而不是组件自身的数据。
官网举例说比如一个元素的可见性,你鼠标一点,他就显示,再一点就隐藏,其实你完全可以将该数据交给组件自身,比如4号组件的数据为isShow,他一会儿显示(true),一会儿隐藏(false),其实你没有必要大动干戈的将数据交给pinia,大家都用该数据时,你才考虑集中式状态管理,这就是为什么要用pinia以及对pinia的理解。
App.vue 备份
<template>
<div class="app">
标题
<Header></Header>
导航区
<div class="navigate">
第一种: to的字符串写法
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/news" active-class="active">新闻</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
第二种: to的对象写法 path跳转
<RouterLink :to="{path:'/home'}" active-class="active">首页</RouterLink>
<RouterLink :to="{path:'/news'}" active-class="active">新闻</RouterLink>
<RouterLink :to="{path:'/about'}" active-class="active">关于</RouterLink>
第三种: to的对象写法 名字跳转
<RouterLink replace :to="{name:'home'}" active-class="active">首页</RouterLink>
<RouterLink replace :to="{name:'news'}" active-class="active">新闻</RouterLink>
<RouterLink replace :to="{name:'about'}" active-class="active">关于</RouterLink>
</div>
展示区
<div class="main-content">
占位符,此处将来要展示不同的组件内容
<RouterView></RouterView>
此处以后可能要展示各种组件内容,到底展示哪个组件,取决于路由的匹配结果
</div>
</div>
</template>
<script setup lang="ts" name="App">
import { RouterView,RouterLink } from 'vue-router';
import Header from '@/components/Header.vue';
</script>
<style scoped>
/*App*/
.navigate {
display: flex;
justify-content: space-around;
margin: 0 100px;
}
.navigate a {
display: block;
text-align: center;
width: 90px;
height: 40px;
line-height: 40px;
border-radius: 10px;
background-color: gray;
text-decoration: none;
color: white;
font-size: 18px;
letter-spacing: 5px;
}
.navigate a.active {
background-color: #64967E;
color: #ffc268;
font-weight: 900;
text-shadow: 0 0 1px black;
font-family: 微软雅黑;
}
.main-content {
margin: 0 auto;
margin-top: 30px;
border-radius: 10px;
width: 90%;
height: 400px;
border: 1px solid;
}
</style>
044_准备一个效果
多个组件共享数据,才考虑用 pinia,接下来我们准备这些做一个效果来看看。
笔记

两个组件

最少两个组件,才能聊出共享
一个组件保存求和的值,一个组件保存着获取一句土味情话

name="App" 这个name是用来定义当前组件名字的,如果你不写,他就会去寻找文件名作为组件名

安装 npm install -D sass-embedded
n为用户选择的数据

当点击2时,2此时为字符串,字符串和数字是不会相加的


只有一上来时,你写的那个初始值,他才是数字,所有用户的输入他默认处理都是字符串,那问题如何让他变成数字呢?有如下三种方式:
1, 在 value 前添加 冒号,红色的都是表达式,一执行就是数字1,2,3

2, 最正统的做法是在 v-model.number="n" 这添加number

3, 在方法里面添加Number() 包裹一下就可以了


安装 npm i axios
土味情话网站,后端已经解决跨域问题
https://api.uomg.com/api/rand.qinghua?format=json
https://api.vvhan.com/api/love



在数组前面添加数据使用unshift()方法
在数组后面添加数据使用push()方法!

打印不是目的,我们要将这句话塞到浏览器上

生成id的第三方库 有两个比较常用 uuid nanoid
安装
npm i nanoid
npm i uuid
引入 nanoid
import { nanoid } from 'nanoid';
使用
id: nanoid()



点击一个他就来一个


_准备一个效果 实现代码如下
1, src/App.vue
<template>
<Count />
<br>
<LoveTalk />
<!-- <div class="app">
标题
<Header></Header>
导航区
<div class="navigate">
第一种: to的字符串写法
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/news" active-class="active">新闻</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
第二种: to的对象写法 path跳转
<RouterLink :to="{path:'/home'}" active-class="active">首页</RouterLink>
<RouterLink :to="{path:'/news'}" active-class="active">新闻</RouterLink>
<RouterLink :to="{path:'/about'}" active-class="active">关于</RouterLink>
第三种: to的对象写法 名字跳转
<RouterLink replace :to="{name:'home'}" active-class="active">首页</RouterLink>
<RouterLink replace :to="{name:'news'}" active-class="active">新闻</RouterLink>
<RouterLink replace :to="{name:'about'}" active-class="active">关于</RouterLink>
</div>
展示区
<div class="main-content">
占位符,此处将来要展示不同的组件内容
<RouterView></RouterView>
此处以后可能要展示各种组件内容,到底展示哪个组件,取决于路由的匹配结果
</div>
</div> -->
</template>
<script setup lang="ts" name="App">
import Count from '@/components/Count.vue';
import LoveTalk from '@/components/LoveTalk.vue';
</script>
<!-- <script setup lang="ts" name="App">
import { RouterView,RouterLink } from 'vue-router';
import Header from '@/components/Header.vue';
</script> -->
<style scoped>
/*
.navigate {
display: flex;
justify-content: space-around;
margin: 0 100px;
}
.navigate a {
display: block;
text-align: center;
width: 90px;
height: 40px;
line-height: 40px;
border-radius: 10px;
background-color: gray;
text-decoration: none;
color: white;
font-size: 18px;
letter-spacing: 5px;
}
.navigate a.active {
background-color: #64967E;
color: #ffc268;
font-weight: 900;
text-shadow: 0 0 1px black;
font-family: 微软雅黑;
}
.main-content {
margin: 0 auto;
margin-top: 30px;
border-radius: 10px;
width: 90%;
height: 400px;
border: 1px solid;
}
*/
</style>
2, src/components/Count.vue
<template>
<div class="count">
<h2>当前求和为: {{ sum }}</h2>
<select v-model="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(1) // 当前求和
let n = ref(1) // 用户选择的数字
// 方法
function add() {
sum.value += Number(n.value)
}
function minus() {
sum.value -= Number(n.value)
}
</script>
<style lang="scss" scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,button {
margin: 0 5px;
height: 25px;
}
</style>
3, src/components/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 setup lang="ts" name="LoveTalk">
import axios from 'axios';
import { reactive } from 'vue';
import { nanoid } from 'nanoid';
// 数据
let talkList = reactive([
{id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
{id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
{id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
]);
// 方法 发送请求获取一句土味情话
async function getLoveTalk() {
// 发送请求获取一句土味情话 连续两次结构加重命名
// let result = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// console.log(result.data.content);
// 把请求回来的数据字符串,包装成对象,添加到数组中
// let obj = {id: nanoid(), title: result.data.content};
let obj = {id: nanoid(), title};// title: title触发对象的简写形式直接写title就可以了
// console.log(obj);
// 添加到数组中
talkList.unshift(obj); // 添加到数组开头位置
// talkList.push({id: nanoid(), title: result.data.content});// 添加到数组末尾位置
}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>


045_搭建pinia环境
安装 npm i pinia
安装pinia结束后,你要去一个地方,编写一段代码,固定的,不允许换位置,接下来我们将pinia的时候,无论是文件夹的划分,还是命名,还是写法,我们都完全遵循官方的规范。
首先,回到src/main.ts里面,
// 第一步 引入 pinia
import {createPinia} from 'pinia'
// 第二 创建 pinia
const pinia = createPinia()
// 第三步 安装 pinia 就是将 pinia 挂载到应用上
app.use(pinia)

_搭建pinia环境 实现代码如下
1, src/App.vue
<template>
<Count />
<br>
<Dog />
<!-- <div class="app">
标题
<Header></Header>
导航区
<div class="navigate">
第一种: to的字符串写法
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/news" active-class="active">新闻</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
第二种: to的对象写法 path跳转
<RouterLink :to="{path:'/home'}" active-class="active">首页</RouterLink>
<RouterLink :to="{path:'/news'}" active-class="active">新闻</RouterLink>
<RouterLink :to="{path:'/about'}" active-class="active">关于</RouterLink>
第三种: to的对象写法 名字跳转
<RouterLink replace :to="{name:'home'}" active-class="active">首页</RouterLink>
<RouterLink replace :to="{name:'news'}" active-class="active">新闻</RouterLink>
<RouterLink replace :to="{name:'about'}" active-class="active">关于</RouterLink>
</div>
展示区
<div class="main-content">
占位符,此处将来要展示不同的组件内容
<RouterView></RouterView>
此处以后可能要展示各种组件内容,到底展示哪个组件,取决于路由的匹配结果
</div>
</div> -->
</template>
<script setup lang="ts" name="App">
import Count from '@/components/Count.vue';
import Dog from '@/components/Dog.vue';
</script>
<!-- <script setup lang="ts" name="App">
import { RouterView,RouterLink } from 'vue-router';
import Header from '@/components/Header.vue';
</script> -->
<style scoped>
/*
.navigate {
display: flex;
justify-content: space-around;
margin: 0 100px;
}
.navigate a {
display: block;
text-align: center;
width: 90px;
height: 40px;
line-height: 40px;
border-radius: 10px;
background-color: gray;
text-decoration: none;
color: white;
font-size: 18px;
letter-spacing: 5px;
}
.navigate a.active {
background-color: #64967E;
color: #ffc268;
font-weight: 900;
text-shadow: 0 0 1px black;
font-family: 微软雅黑;
}
.main-content {
margin: 0 auto;
margin-top: 30px;
border-radius: 10px;
width: 90%;
height: 400px;
border: 1px solid;
}
*/
</style>
2, src/main.ts
// 引入 createApp 用于创建应用
import {createApp} from 'vue'
// 引入 App根组件
import App from './App.vue'
// 第一步 引入 pinia
import {createPinia} from 'pinia'
// 引入路由器
// import router from './router'
// 创建一个应用
const app = createApp(App)
// 第二 创建 pinia
const pinia = createPinia()
// 第三步 安装 pinia 就是将 pinia 挂载到应用上
app.use(pinia)
// app.use(createPinia()) // 也可以这样写,等同于上面的写法 简写写法
// 使用路由器
// app.use(router)
// 挂载整个应用到 app 容器中
app.mount('#app')
3, src/components/Count.vue
<template>
<div class="count">
<h2>当前求和为: {{ sum }}</h2>
<select v-model="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(1) // 当前求和
let n = ref(1) // 用户选择的数字
// 方法
function add() {
sum.value += Number(n.value)
}
function minus() {
sum.value -= Number(n.value)
}
</script>
<style lang="scss" scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,button {
margin: 0 5px;
height: 25px;
}
</style>
4, src/components/Dog.vue
<template>
<div class="dog">
<img v-for="(dog,index) in dogList" :key="index" :src="dog" alt="">
<br>
<button @click="getDog">再来一只小狗</button>
</div>
</template>
<script setup lang="ts" name="Dog">
import { reactive } from 'vue'
import axios from 'axios'
// 数据
let dogList = reactive([
'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
])
// 方法
async function getDog() {
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
dogList.push(result.data.message)
} catch (error) {
alert(error)
}
}
</script>
<style scoped>
.dog {
background-color: pink;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
img {
height: 150px;
margin-right: 10px;
}
</style>

046_存储+读取数据
5.3 [存储+读取数据]
1, Store是一个保存: 状态,业务逻辑 的实体,每个组件都可以读取,写入他。
2, 他有三个概念: state,getter,action,相当于组件中的: data,computed和methods。
3, 具体编码: src/store/count.ts
// 引入 defineStore 用于创建 store
import {defineStore} from 'pinia'
// 定义并暴露一个 store
export const useCountStore = defineStore('count',{
// 动作
actions: {},
// 状态
state() {
return {
sum:6
}
},
getters: {}
})
笔记




在 src 目录下创建 store

store就是pinia的具体体现

store就是pinia落地的东西 store就是pinia里面的总经理

在 store 目录下创建 count.ts 有人也用 Count.ts,这也是可以的

总之你取名就是要让人知道你在里面存储的是什么

看到红色你就应该捕获到该项目用到pinia

而且都是跟计数相关的内容

当你在store目录下看到 user.ts 这里面肯定是跟用户相关的数据

我们去count.ts建立一个属于count的小仓库 存储count相关的数据
import {defineStore} from 'pinia'
const useCountStore = defineStore('count',{
// 配置对象
state:()=>{
return {
count:0
}
},
actions:{
increment(){this.count++},//这里的this指向的是当前store的实例对象,可以直接操作里面的数据
decrement(){this.count--}
},
getters:{
doubleCount:state=>{return state.count*2}
}
})
代码解释:
defineStore('参数1',{参数2})
参数1: 相当于他的id值,这个名跟文件名保持一致,
参数2: 写一个配置对象
state: 状态(数据),官方要求这里必须写成一个函数
这里用 export 实现分别暴露

此时你拥有了一个pinia下在state下存储count 的组织架构

count.ts里面就是一个仓库,只要是跟统计相关的(和,差,乘,除,开根后的结果等)都可以存储到这里

代码-效果

countStore 是reactive所定义的一个响应式对象



$state 就是我们刚才在 count.ts 文件中配置的state


如果你自己定义的ref,撤包了,你必须使用 'xxx.value'格式获取数据



如果你是在 reactive里面包裹的ref(),他就直接给你撤包了解除来了,c后面就不需要'.value'

由于sum是包裹在reactive()整个蓝色里面的,所以此时的sum就不需要.value了

由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法 不需要解构赋值,也不需要.value访问

直接拿到state中的数据了
import { ref,reactive } from 'vue'
import { useCountStore } from '@/store/count'
// 引入store中的数据和方法
const countStore = useCountStore()
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问
console.log('@@@',countStore.sum)


还有一种方式,可以从$state里面去拿到state的数据,这种方式在编程式开发中用得很多
import { ref,reactive } from 'vue'
import { useCountStore } from '@/store/count'
// 引入store中的数据和方法
const countStore = useCountStore()
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问 第一种拿到sum数据的方式
// console.log('@@@',countStore.sum) // 响应式对象数据,可以直接访问里面的数据和方法
console.log('@@@',countStore.$state.sum)


第三种方式 拿到 state 中的数据
import { ref,reactive,computed } from 'vue'
import { useCountStore } from '@/store/count'
// 引入store中的数据和方法
const countStore = useCountStore()
// 第三种拿到sum数据的方式
let sum = computed(()=>{
return countStore.sum
})
console.log('@@@',countStore.$state.sum)

因为我们定义了 countStore,所以我们可以直接拿到state中的sum数据

此时state中的sum数据可以读出来了



pinia 主体

count 是pinia里面的小分类

src/store/dog.ts
import {defineStore} from 'pinia'
export const useDogStore = defineStore('dog',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
dogList:[
'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
]
}
}
})

src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
sum: 8
}
}
})

src/store/loveTalk.ts
import {defineStore} from 'pinia'
export const useTalkStore = defineStore('talk',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
talkList:[
{id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
{id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
{id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
]
}
}
})

定义仓库: useTalkStore ,分类ID: 'talk',数据: talkList

src/components/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 setup lang="ts" name="LoveTalk">
import axios from 'axios';
import { reactive } from 'vue';
import { nanoid } from 'nanoid';
// 数据
// let talkList = reactive([
// {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// ]);
// 方法 发送请求获取一句土味情话
async function getLoveTalk() {
// 发送请求获取一句土味情话 连续两次结构加重命名
// let result = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// 1. let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// console.log(result.data.content);
// 把请求回来的数据字符串,包装成对象,添加到数组中
// let obj = {id: nanoid(), title: result.data.content};
// 2. let obj = {id: nanoid(), title};// title: title触发对象的简写形式直接写title就可以了
// console.log(obj);
// 添加到数组中
// 3. talkList.unshift(obj); // 添加到数组开头位置
// talkList.push({id: nanoid(), title: result.data.content});// 添加到数组末尾位置
}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>


count与dog都已经安装了


_存储+读取数据 实现代码如下:
1, src/App.vue
<template>
<Count />
<br>
<Dog />
<!-- <div class="app">
标题
<Header></Header>
导航区
<div class="navigate">
第一种: to的字符串写法
<RouterLink to="/home" active-class="active">首页</RouterLink>
<RouterLink to="/news" active-class="active">新闻</RouterLink>
<RouterLink to="/about" active-class="active">关于</RouterLink>
第二种: to的对象写法 path跳转
<RouterLink :to="{path:'/home'}" active-class="active">首页</RouterLink>
<RouterLink :to="{path:'/news'}" active-class="active">新闻</RouterLink>
<RouterLink :to="{path:'/about'}" active-class="active">关于</RouterLink>
第三种: to的对象写法 名字跳转
<RouterLink replace :to="{name:'home'}" active-class="active">首页</RouterLink>
<RouterLink replace :to="{name:'news'}" active-class="active">新闻</RouterLink>
<RouterLink replace :to="{name:'about'}" active-class="active">关于</RouterLink>
</div>
展示区
<div class="main-content">
占位符,此处将来要展示不同的组件内容
<RouterView></RouterView>
此处以后可能要展示各种组件内容,到底展示哪个组件,取决于路由的匹配结果
</div>
</div> -->
</template>
<script setup lang="ts" name="App">
import Count from '@/components/Count.vue';
import Dog from '@/components/Dog.vue';
</script>
<!-- <script setup lang="ts" name="App">
import { RouterView,RouterLink } from 'vue-router';
import Header from '@/components/Header.vue';
</script> -->
<style scoped>
/*
.navigate {
display: flex;
justify-content: space-around;
margin: 0 100px;
}
.navigate a {
display: block;
text-align: center;
width: 90px;
height: 40px;
line-height: 40px;
border-radius: 10px;
background-color: gray;
text-decoration: none;
color: white;
font-size: 18px;
letter-spacing: 5px;
}
.navigate a.active {
background-color: #64967E;
color: #ffc268;
font-weight: 900;
text-shadow: 0 0 1px black;
font-family: 微软雅黑;
}
.main-content {
margin: 0 auto;
margin-top: 30px;
border-radius: 10px;
width: 90%;
height: 400px;
border: 1px solid;
}
*/
</style>
2, src/main.ts
// 引入 createApp 用于创建应用
import {createApp} from 'vue'
// 引入 App根组件
import App from './App.vue'
// 第一步 引入 pinia
import {createPinia} from 'pinia'
// 引入路由器
// import router from './router'
// 创建一个应用
const app = createApp(App)
// 第二 创建 pinia
const pinia = createPinia()
// 第三步 安装 pinia 就是将 pinia 挂载到应用上
app.use(pinia)
// app.use(createPinia()) // 也可以这样写,等同于上面的写法 简写写法
// 使用路由器
// app.use(router)
// 挂载整个应用到 app 容器中
app.mount('#app')
3, src/components/Dog.vue
<template>
<div class="dog">
<img v-for="(dog,index) in dogStore.dogList" :key="index" :src="dog" alt="">
<br>
<button @click="getDog">再来一只小狗</button>
</div>
</template>
<script setup lang="ts" name="Dog">
import { reactive } from 'vue'
import axios from 'axios'
import {useDogStore} from '@/store/dog.ts'
const dogStore = useDogStore()
// 数据
// let dogList = reactive([
// 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// ])
// 方法
async function getDog() {
// try {
// let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// dogList.push(result.data.message)
// } catch (error) {
// alert(error)
// }
}
</script>
<style scoped>
.dog {
background-color: pink;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
img {
height: 150px;
margin-right: 10px;
}
</style>
4, src/components/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 setup lang="ts" name="LoveTalk">
import axios from 'axios';
import { reactive } from 'vue';
import { nanoid } from 'nanoid';
import {useTalkStore} from '@/store/loveTalk.ts'
const talkStore = useTalkStore()
// console.log('@',talkStore)
// 数据
// let talkList = reactive([
// {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// ]);
// 方法 发送请求获取一句土味情话
async function getLoveTalk() {
// 发送请求获取一句土味情话 连续两次结构加重命名
// let result = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// 1. let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// console.log(result.data.content);
// 把请求回来的数据字符串,包装成对象,添加到数组中
// let obj = {id: nanoid(), title: result.data.content};
// 2. let obj = {id: nanoid(), title};// title: title触发对象的简写形式直接写title就可以了
// console.log(obj);
// 添加到数组中
// 3. talkList.unshift(obj); // 添加到数组开头位置
// talkList.push({id: nanoid(), title: result.data.content});// 添加到数组末尾位置
}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
5, src/components/Count.vue
<template>
<div class="count">
<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="add">加</button>
<button @click="minus">减</button>
</div>
</template>
<script setup lang="ts" name="Count">
import { ref,reactive,computed } from 'vue'
import { useCountStore } from '@/store/count'
// 引入store中的数据和方法
const countStore = useCountStore()
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问
// 以下三种方式都可以拿到state中的数据
// 第一种拿到sum数据的方式
// console.log('@@@',countStore.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第二种拿到sum数据的方式
// console.log('@@@',countStore.$state.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第三种拿到sum数据的方式
// let sum = computed(()=>{
// return countStore.sum
// })
// console.log('@@@',countStore.$state.sum)
/*
let obj = reactive({
a:1,
b:2,
c:ref(3)
})
let x = ref(4)
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // 撤包了 不需要.value访问,直接访问即可
console.log(x.value) // 需要.value访问
*/
// 数据
let n = ref(1) // 用户选择的数字
// 方法
function add() {
}
function minus() {
}
</script>
<style lang="scss" scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,button {
margin: 0 5px;
height: 25px;
}
</style>
6, src/store/dog.ts
import {defineStore} from 'pinia'
export const useDogStore = defineStore('dog',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
dogList:[
'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
]
}
}
})
7, src/store/loveTalk.ts
import {defineStore} from 'pinia'
export const useTalkStore = defineStore('talk',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
talkList:[
{id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
{id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
{id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
]
}
}
})
8, src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
sum: 8
}
}
})

047_修改数据(三种方式)
5.4 [修改数据(三种方式)]
1, 第一种修改方式,直接修改
countStore.sum = 666
2, 第二种修改方式: 批量修改
countStore.$patch({
sum:999,
school:'atguigu'
})
3, 第三种方式: 借助actions修改(actions中可以编写一些业务逻辑)
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{
/********/
actions: {
// 加
increment(value:number) {
if(this.sum<10) {
// 操作 useCountStore 中的sum
this.sum += value
}
}
},
})

笔记


代码-效果



完整的碎片: 绿色框里面的内容

发生3次改变

Mouse: 鼠标事件
Keyboard: 键盘事件
Components: 组件上相关事件

actions 第三种修改数据方式

count.ts 是存储跟计数相关的小仓库

执行 代码-效果


你能在 actions对象里面访问到 state 吗?肯定不可以,这是一个作用域的问题

底层帮你维护好了一个this,我们先输出一个this,看看里面都有什么


this.$state


给我们程序员用的基本都是设计为$开头

获取数据有两种方法 this.sum和$state.sum


要做一些极限的限制,你需要这样写


这样也可以实现限制

商品订单的操作 秒杀 参与活动 中奖 抽奖等

设计订单逻辑处理actions,这样就涉及到代码复用了

_修改数据(三种方式) 实现代码如下
1, src/components/Count.vue
<template>
<div class="count">
<h2>当前求和为: {{ countStore.sum }}</h2>
<h3>欢迎来到: {{ countStore.school }},坐落于: {{ countStore.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 setup lang="ts" name="Count">
import { ref,reactive,computed } from 'vue'
// 引入 useCountStore
import { useCountStore } from '@/store/count'
// 使用useCountStore,得到一个专门保存 count 相关的 store 实例对象 countStore
const countStore = useCountStore()
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问
// 以下三种方式都可以拿到state中的数据
// 第一种拿到sum数据的方式
// console.log('@@@',countStore.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第二种拿到sum数据的方式
// console.log('@@@',countStore.$state.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第三种拿到sum数据的方式
// let sum = computed(()=>{
// return countStore.sum
// })
// console.log('@@@',countStore.$state.sum)
/*
let obj = reactive({
a:1,
b:2,
c:ref(3)
})
let x = ref(4)
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // 撤包了 不需要.value访问,直接访问即可
console.log(x.value) // 需要.value访问
*/
// 数据
let n = ref(1) // 用户选择的数字
// 方法
function add() {
// 响应式对象数据,可以直接访问里面的数据和方法
// 不需要解构赋值,也不需要.value访问
// countStore.sum += n.value
// 第一种修改数据的方式
// countStore.sum += 1
// countStore.school = '尚硅谷'
// countStore.address = '北京'
// 第二种批量修改数据的方式
// countStore.$patch({
// sum: countStore.sum + 1,
// school: '尚硅谷',
// address: '北京'
// })
// 第三种修改数据的方式
countStore.increment(n.value)
}
function minus() {
}
</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>
2, src/components/Dog.vue
<template>
<div class="dog">
<img v-for="(dog,index) in dogStore.dogList" :key="index" :src="dog" alt="">
<br>
<button @click="getDog">再来一只小狗</button>
</div>
</template>
<script setup lang="ts" name="Dog">
import { reactive } from 'vue'
import axios from 'axios'
import {useDogStore} from '@/store/dog.ts'
const dogStore = useDogStore()
// 数据
// let dogList = reactive([
// 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// ])
// 方法
async function getDog() {
// try {
// let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// dogList.push(result.data.message)
// } catch (error) {
// alert(error)
// }
}
</script>
<style scoped>
.dog {
background-color: pink;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
img {
height: 150px;
margin-right: 10px;
}
</style>
3, src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{ // 配置对象
// actions里面放置一个一个的动作方法,用于响应式组件中的"动作(比如要加,要减)" 用来修改数据
actions: {
increment(value:number) { // 修改数据
console.log('increment被调用了',value)
// 如何修改数据 有两种方式修改数据 使用 this.sum 或者使用this.$state.sum 都可以修改数据
// console.log(this.$state.sum)
// console.log(this.sum) // 8 拿到sum的值 8
// this 是当前的store实例对象,this.sum 就是访问store里面的数据,this.sum++ 就是修改数据
if (this.sum < 10) {
this.sum += value;
}
// this.sum++
}
},
// 真正存储数据的地方
state:()=>{
return {
sum: 8,
school: 'atguigu',
address: '宏福科技园'
}
}
})
4, src/store/dog.ts
import {defineStore} from 'pinia'
export const useDogStore = defineStore('dog',{ // 配置对象
// 真正存储数据的地方
state:()=>{
return {
dogList:[
'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
]
}
}
})

048_storeToRefs
5.5 [storeToRefs]
1, 借助 storeToRefs将store中的数据转为ref对象,方便在模板中使用。
2, 注意: pinia提供的storeToRefs只会将数据做转换, 而vue的toRefs会转换store中所有数据和方法。
1, src/components/Count.vue
<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>
笔记








这句绿色代码确实把数据拿出来了,可数据丢失了响应式

添加 toRefs(countStore) 数据还真变成了响应式,实际开发中是不会用toRefs() 来包裹stroe的数据



打印一下,发现他把store身上所有的数据,方法等都变成了ref响应式数据


我们只需要将school,sum等数据包裹一下就可以了,increment是方法,不需要包裹

所以我们不能用toRefs()来包裹 store里面的存储库,如何解决这个问题呢?我们可以在pinia中引入 storeToRefs来解决这个问题
1, 引入 storeToRefs 引入 useCountStore
import { useCountStore } from '@/store/count'
import { storeToRefs } from 'pinia'
2,使用
const countStore = useCountStore()
const {sum,school,address} = storeToRefs(countStore)
可以完美解决数据解构赋值问题


总结:
storeToRefs() 只关注 store里面的数据,不关注store里面的方法





_storeToRefs 实现代码如下:
1, src/components/Dog.vue
<template>
<div class="dog">
<img v-for="(dog,index) in dogList" :key="index" :src="dog" alt="">
<br>
<button @click="getDog">再来一只小狗</button>
</div>
</template>
<script setup lang="ts" name="Dog">
import {useDogStore} from '@/store/dog.ts'
import { storeToRefs } from 'pinia';
const dogStore = useDogStore()
const {dogList} = storeToRefs(dogStore)
// 数据
// let dogList = reactive([
// 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// ])
// 方法
async function getDog() {
dogStore.addDog()
// try {
// let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// dogList.push(result.data.message)
// } catch (error) {
// alert(error)
// }
}
</script>
<style scoped>
.dog {
background-color: pink;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
img {
height: 150px;
margin-right: 10px;
}
</style>
2, src/components/Count.vue
<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 setup lang="ts" name="Count">
import { ref,reactive,computed,toRefs } from 'vue'
import { storeToRefs } from 'pinia'
// 引入 useCountStore
import { useCountStore } from '@/store/count'
// 使用useCountStore,得到一个专门保存 count 相关的 store 实例对象 countStore
const countStore = useCountStore()
// 使用storeToRefs只会关注store中的数据,不会对方法进行ref包裹 解构赋值,可以直接访问里面的数据和方法
const {sum,school,address} = storeToRefs(countStore)
// console.log('!!!',storeToRefs(countStore))
// const {sum,school,address} = toRefs(countStore)
// console.log('!!!',toRefs(countStore))
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问
// 以下三种方式都可以拿到state中的数据
// 第一种拿到sum数据的方式
// console.log('@@@',countStore.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第二种拿到sum数据的方式
// console.log('@@@',countStore.$state.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第三种拿到sum数据的方式
// let sum = computed(()=>{
// return countStore.sum
// })
// console.log('@@@',countStore.$state.sum)
/*
let obj = reactive({
a:1,
b:2,
c:ref(3)
})
let x = ref(4)
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // 撤包了 不需要.value访问,直接访问即可
console.log(x.value) // 需要.value访问
*/
// 数据
let n = ref(1) // 用户选择的数字
// 方法
function add() {
// 响应式对象数据,可以直接访问里面的数据和方法
// 不需要解构赋值,也不需要.value访问
// countStore.sum += n.value
// 第一种修改数据的方式
// countStore.sum += 1
// countStore.school = '尚硅谷'
// countStore.address = '北京'
// 第二种批量修改数据的方式
// countStore.$patch({
// sum: countStore.sum + 1,
// school: '尚硅谷',
// address: '北京'
// })
// 第三种修改数据的方式
countStore.increment(n.value)
}
function minus() {
countStore.decrement(n.value)
// 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>
3, src/components/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 setup lang="ts" name="LoveTalk">
import {useTalkStore} from '@/store/loveTalk.ts'
import { storeToRefs } from 'pinia';
const talkStore = useTalkStore()
const { talkList } = storeToRefs(talkStore)
// console.log('@',talkStore)
// 数据
// let talkList = reactive([
// {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// ]);
// 方法 发送请求获取一句土味情话
function getLoveTalk() {
talkStore.getAddTalk()
}
//async function getLoveTalk() {
// 发送请求获取一句土味情话 连续两次结构加重命名
// let result = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// 1. let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// console.log(result.data.content);
// 把请求回来的数据字符串,包装成对象,添加到数组中
// let obj = {id: nanoid(), title: result.data.content};
// 2. let obj = {id: nanoid(), title}; // title: title触发对象的简写形式直接写title就可以了
// console.log(obj);
// 添加到数组中
// 3. talkList.unshift(obj); // 添加到数组开头位置
// talkList.push({id: nanoid(), title: result.data.content});// 添加到数组末尾位置
//}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
4, src/store/dog.ts
import {defineStore} from 'pinia'
import axios from 'axios'
export const useDogStore = defineStore('dog',{ // 配置对象
actions:{
async addDog(){
// this.dogList.push(url)
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
this.dogList.push(result.data.message)
} catch (error) {
alert(error)
}
}
},
// 真正存储数据的地方
state:()=>{
return {
dogList:[
'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
]
}
}
})
5, src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{ // 配置对象
// actions里面放置一个一个的动作函数或者方法,用于响应式组件中的"动作(比如要加,要减)" 用来修改数据
actions: {
increment(value:number) { // 修改数据
// 1, console.log('increment被调用了',value)
// 2, console.log(this)
// 如何修改数据 有两种方式修改数据 使用 this.sum 或者使用this.$state.sum 都可以修改数据
// console.log(this.$state.sum) // 8 拿到sum的值 8
// console.log(this.sum) // 8 拿到sum的值 8
// this 是当前的store实例对象,this.sum 就是访问store里面的数据,this.sum++ 就是修改数据
if (this.sum < 10) {
this.sum += value;
}
},
decrement(value:number) {
console.log('decrement被调用了',value)
if (this.sum > 0) {
this.sum -= value;
}
},
reset() {
// 重置数据
this.sum = 0;
this.school = '北京大学';
}
},
// 真正存储数据的地方
state:()=>{
return {
sum: 6,
school: 'atguigu',
address: '宏福科技园'
}
}
})
6, src/store/loveTalk.ts
import {defineStore} from 'pinia'
import axios from 'axios';
import { nanoid } from 'nanoid';
export const useTalkStore = defineStore('talk',{ // 配置对象
actions:{
async getAddTalk() {
let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
let obj = {id: nanoid(), title}; // 生成一个唯一的ID,用于唯一标识对话内容
this.talkList.unshift(obj); // 添加到数组开头位置,实现最新对话在最上方显示的效果
}
// // 添加一个对话
// addTalk(title:string){ // 添加对话
// const talk = {id: Date.now().toString(), title}
// this.talkList.push(talk)
// },
// // 删除对话
// delTalk(id:string){ // 根据ID删除对话
// const index = this.talkList.findIndex((item)=> item.id === id)
// if(index !== -1) {
// this.talkList.splice(index, 1)
// }
// },
// // 清空对话
// clearTalk(){ // 清空所有对话
// this.talkList = []
// }
},
// 真正存储数据的地方
state:()=>{
return {
talkList:[
{id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
{id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
{id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
]
}
}
})

049_getters的使用
有一天你对pinia里面的数据不满意时,就可以追加 getters,vuex里面也有getters.
5.6 [getters]
1, 概念: 当 state 中的数据,需要经过处理后再使用时,可以使用getters配置。
2, 追加 gettere 配置
// 引入 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,
// bigSum:(state):number => state.sum*10,
upperSchool():string{
return this.school.toUpperCase()
}
}
})
笔记





050_$subscribe的使用
这节课我们来说一下订阅,他可以让你监视state里面数据极其变化。
5.7 [$subscribe]
通过 store 的 $subscribe() 方法侦听 state 极其变化
talkStore.$subscribe((mutate,state)=>{
console.log('LoveTalk',mutate,state)
localStorage.setItem('talk',JSON.stringify(talkList.value))
})
笔记





代码-效果


代码-效果



发现一个尴尬的情况: localStore 里面存储的都是字符串,如果你传的不是字符串,底层会调用toString(),如果一个数据里面都是对象,调用了toString()必定是[object Object],[object Object]...所以你要真想存储,我们就得使用 JSON.stringify(state.talkList)此时飘红就解决了
代码如下:
localStorage.setItem('talkList',JSON.stringify(state.talkList))
代码的意思: 我要存储数组,并且要打成字符串的形式



存储数组且打成字符串的形式


能存储到本地,我们就可以办大事了,可以实现刷新不丢失数据

初始值的时候,不要写死这三个,那怎么办?我们存储什么就去拿什么localStorage.getItem('talk'),但是你光拿不行,

光拿是很危险的,就变成了如下如图所示的点...,你有这么多数据吗?不是的,而是你这么拿的话,拿出来的是一个字符串,字符串一旦参与v-for遍历,就变成了如下如图所示的点...

所以说,你拿出来的那一堆数据要将他转换一下,当时你把字符串存储进去的,你现在就得把字符串转换回来,使用 JSON.parse(localStorage.getItem('talkList') as string) || []
代码-效果




你有可能取不出来,类型有问题,

我们可以使用 as string 直接断言,他就是一个能取出来的字符串

此时上面的代码还是不完善,有可能人家第一次使用你家的网站,key里面可能没有任何东西,你可以刷新,他什么都没有,但是你如果点击按钮,立马报错,你不能从null身上读取 unshift()

因为你把null交给 JSON.parse()解析完成后还是null,

你点击按钮就是添加,此时就是 null.unshift,你是不是在null身上读取unshift,所以你就有问题了。

如何处理,我们可以写或者一个空数组,就可以解决 null.unshift问题,你里面有数据我们就解析,没有就使用空数组

效果如下,完美解决问题



_$subscribe和getters的使用 实现代码如下
1, src/components/Dog.vue
<template>
<div class="dog">
<img v-for="(dog,index) in dogList" :key="index" :src="dog" alt="">
<br>
<button @click="getDog">再来一只小狗</button>
</div>
</template>
<script setup lang="ts" name="Dog">
import {useDogStore} from '@/store/dog.ts'
import { storeToRefs } from 'pinia';
const dogStore = useDogStore()
const {dogList} = storeToRefs(dogStore)
dogStore.$subscribe((mutate,state)=>{
console.log('dogStore里面保存的数据发生变化了',mutate,state)
// 持久化存储数据到本地存储中
// localStorage.setItem('dogList',state.dogList)
localStorage.setItem('dogList',JSON.stringify(state.dogList))
})
// 数据
// let dogList = reactive([
// 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// ])
// 方法
async function getDog() {
dogStore.addDog()
// try {
// let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// dogList.push(result.data.message)
// } catch (error) {
// alert(error)
// }
}
</script>
<style scoped>
.dog {
background-color: pink;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
img {
height: 150px;
margin-right: 10px;
}
</style>
2, src/components/Count.vue
<template>
<div class="count">
<h2>当前求和为: {{ sum }},放大10倍: {{ 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 setup lang="ts" name="Count">
import { ref,reactive,computed,toRefs } from 'vue'
import { storeToRefs } from 'pinia'
// 引入 useCountStore
import { useCountStore } from '@/store/count'
// 使用useCountStore,得到一个专门保存 count 相关的 store 实例对象 countStore
const countStore = useCountStore()
// 使用storeToRefs只会关注store中的数据,不会对方法进行ref包裹 解构赋值,可以直接访问里面的数据和方法
const {sum,school,address,bigSum,upperSchool} = storeToRefs(countStore)
// console.log('!!!',storeToRefs(countStore))
// const {sum,school,address} = toRefs(countStore)
// console.log('!!!',toRefs(countStore))
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问
// 以下三种方式都可以拿到state中的数据
// 第一种拿到sum数据的方式
// console.log('@@@',countStore.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第二种拿到sum数据的方式
// console.log('@@@',countStore.$state.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第三种拿到sum数据的方式
// let sum = computed(()=>{
// return countStore.sum
// })
// console.log('@@@',countStore.$state.sum)
/*
let obj = reactive({
a:1,
b:2,
c:ref(3)
})
let x = ref(4)
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // 撤包了 不需要.value访问,直接访问即可
console.log(x.value) // 需要.value访问
*/
// 数据
let n = ref(1) // 用户选择的数字
// 方法
function add() {
// 响应式对象数据,可以直接访问里面的数据和方法
// 不需要解构赋值,也不需要.value访问
// countStore.sum += n.value
// 第一种修改数据的方式
// countStore.sum += 1
// countStore.school = '尚硅谷'
// countStore.address = '北京'
// 第二种批量修改数据的方式
// countStore.$patch({
// sum: countStore.sum + 1,
// school: '尚硅谷',
// address: '北京'
// })
// 第三种修改数据的方式
countStore.increment(n.value)
}
function minus() {
countStore.decrement(n.value)
// 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>
3, src/components/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 setup lang="ts" name="LoveTalk">
import {useTalkStore} from '@/store/loveTalk.ts'
import { storeToRefs } from 'pinia';
const talkStore = useTalkStore()
const { talkList } = storeToRefs(talkStore)
talkStore.$subscribe((mutate,state)=>{
console.log('talkStore保存的数据变化了',mutate,state)
// 浏览器的本地存储,持久化数据保存到本地存储中
localStorage.setItem('talkList',JSON.stringify(state.talkList))
})
// talkStore.$subscribe((mutation, state) => {
// if (mutation.type === 'getAddTalk') {
// console.log('数据更新了', state)
// }
// })
// console.log('@',talkStore)
// 数据
// let talkList = reactive([
// {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// ]);
// 方法 发送请求获取一句土味情话
function getLoveTalk() {
talkStore.getAddTalk()
}
//async function getLoveTalk() {
// 发送请求获取一句土味情话 连续两次结构加重命名
// let result = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// 1. let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// console.log(result.data.content);
// 把请求回来的数据字符串,包装成对象,添加到数组中
// let obj = {id: nanoid(), title: result.data.content};
// 2. let obj = {id: nanoid(), title}; // title: title触发对象的简写形式直接写title就可以了
// console.log(obj);
// 添加到数组中
// 3. talkList.unshift(obj); // 添加到数组开头位置
// talkList.push({id: nanoid(), title: result.data.content});// 添加到数组末尾位置
//}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
4, src/store/dog.ts
import {defineStore} from 'pinia'
import axios from 'axios'
export const useDogStore = defineStore('dog',{ // 配置对象
actions:{
async addDog(){
// this.dogList.push(url)
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
this.dogList.push(result.data.message)
} catch (error) {
alert(error)
}
}
},
// 真正存储数据的地方
state:()=>{
return {
dogList:JSON.parse(localStorage.getItem('dogList') as string)||[]
// 2, dogList:JSON.parse(localStorage.getItem('dogList') as string||'[]'),
// 3, dogList:localStorage.getItem('dogList')?JSON.parse(localStorage.getItem('dogList')!):[]
// dogList:[
// 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// ]
}
}
})
5, src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{ // 配置对象
// actions里面放置一个一个的动作函数或者方法,用于响应式组件中的"动作(比如要加,要减)" 用来修改数据
actions: {
increment(value:number) { // 修改数据
// 1, console.log('increment被调用了',value)
// 2, console.log(this)
// 如何修改数据 有两种方式修改数据 使用 this.sum 或者使用this.$state.sum 都可以修改数据
// console.log(this.$state.sum) // 8 拿到sum的值 8
// console.log(this.sum) // 8 拿到sum的值 8
// this 是当前的store实例对象,this.sum 就是访问store里面的数据,this.sum++ 就是修改数据
if (this.sum < 10) {
this.sum += value;
}
},
decrement(value:number) {
console.log('decrement被调用了',value)
if (this.sum > 0) {
this.sum -= value;
}
},
reset() {
// 重置数据
this.sum = 0;
this.school = '北京大学';
}
},
// 真正存储数据的地方
state:()=>{
return {
sum: 6,
school: 'atguigu',
address: '宏福科技园'
}
},
getters: {
// 计算属性
bigSum:state => state.sum * 10,
// bigSum(state) {
// return state.sum * 10; // 计算属性,返回一个新的值
// },
upperSchool():string {
console.log('@@@@',this);
// return state.school.toUpperCase();
return this.school.toUpperCase(); // 计算属性,返回一个新的值
},
// 计算属性,返回一个新的值
// ...... 其他的计算属性
}
})
6, src/store/loveTalk.ts
import {defineStore} from 'pinia'
import axios from 'axios';
import { nanoid } from 'nanoid';
export const useTalkStore = defineStore('talk',{ // 配置对象
actions:{
async getAddTalk() {
let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
let obj = {id: nanoid(), title}; // 生成一个唯一的ID,用于唯一标识对话内容
this.talkList.unshift(obj); // 添加到数组开头位置,实现最新对话在最上方显示的效果
}
// // 添加一个对话
// addTalk(title:string){ // 添加对话
// const talk = {id: Date.now().toString(), title}
// this.talkList.push(talk)
// },
// // 删除对话
// delTalk(id:string){ // 根据ID删除对话
// const index = this.talkList.findIndex((item)=> item.id === id)
// if(index !== -1) {
// this.talkList.splice(index, 1)
// }
// },
// // 清空对话
// clearTalk(){ // 清空所有对话
// this.talkList = []
// }
},
// 真正存储数据的地方
state:()=>{
return {
talkList:JSON.parse(localStorage.getItem('talkList') as string) || []
// 2, talkList:JSON.parse(localStorage.getItem('talkList') as string || '[]')
// 3, talkList:localStorage.getItem('talkList') ? JSON.parse(localStorage.getItem('talkList')!) : []
// talkList:[
// {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// ]
}
}
})

051_store组合式写法
红色的是一个对象,绿色的是选项===>选项式写法



组合式写法

绿色框里面相当于setup函数



_store组合式写法 实现代码如下
1, src/components/Dog.vue
<template>
<div class="dog">
<img v-for="(dog,index) in dogList" :key="index" :src="dog" alt="">
<br>
<button @click="getDog">再来一只小狗</button>
</div>
</template>
<script setup lang="ts" name="Dog">
import {useDogStore} from '@/store/dog.ts'
import { storeToRefs } from 'pinia';
const dogStore = useDogStore()
const {dogList} = storeToRefs(dogStore)
dogStore.$subscribe((mutate,state)=>{
console.log('dogStore里面保存的数据发生变化了',mutate,state)
// 持久化存储数据到本地存储中
// localStorage.setItem('dogList',state.dogList)
localStorage.setItem('dogList',JSON.stringify(state.dogList))
})
// 数据
// let dogList = reactive([
// 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// ])
// 方法
async function getDog() {
dogStore.addDog()
// try {
// let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// dogList.push(result.data.message)
// } catch (error) {
// alert(error)
// }
}
</script>
<style scoped>
.dog {
background-color: pink;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
button {
margin: 0 5px;
}
li {
font-size: 20px;
}
img {
height: 150px;
margin-right: 10px;
}
</style>
2, src/components/Count.vue
<template>
<div class="count">
<h2>当前求和为: {{ sum }},放大10倍: {{ 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 setup lang="ts" name="Count">
import { ref,reactive,computed,toRefs } from 'vue'
import { storeToRefs } from 'pinia'
// 引入 useCountStore
import { useCountStore } from '@/store/count'
// 使用useCountStore,得到一个专门保存 count 相关的 store 实例对象 countStore
const countStore = useCountStore()
// 使用storeToRefs只会关注store中的数据,不会对方法进行ref包裹 解构赋值,可以直接访问里面的数据和方法
const {sum,school,address,bigSum,upperSchool} = storeToRefs(countStore)
// console.log('!!!',storeToRefs(countStore))
// const {sum,school,address} = toRefs(countStore)
// console.log('!!!',toRefs(countStore))
// 由于 countStore 返回的是响应式对象数据,所以可以直接访问里面的数据和方法,
// 不需要解构赋值,也不需要.value访问
// 以下三种方式都可以拿到state中的数据
// 第一种拿到sum数据的方式
// console.log('@@@',countStore.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第二种拿到sum数据的方式
// console.log('@@@',countStore.$state.sum) // 响应式对象数据,可以直接访问里面的数据和方法
// 第三种拿到sum数据的方式
// let sum = computed(()=>{
// return countStore.sum
// })
// console.log('@@@',countStore.$state.sum)
/*
let obj = reactive({
a:1,
b:2,
c:ref(3)
})
let x = ref(4)
console.log(obj.a)
console.log(obj.b)
console.log(obj.c) // 撤包了 不需要.value访问,直接访问即可
console.log(x.value) // 需要.value访问
*/
// 数据
let n = ref(1) // 用户选择的数字
// 方法
function add() {
// 响应式对象数据,可以直接访问里面的数据和方法
// 不需要解构赋值,也不需要.value访问
// countStore.sum += n.value
// 第一种修改数据的方式
// countStore.sum += 1
// countStore.school = '尚硅谷'
// countStore.address = '北京'
// 第二种批量修改数据的方式
// countStore.$patch({
// sum: countStore.sum + 1,
// school: '尚硅谷',
// address: '北京'
// })
// 第三种修改数据的方式
countStore.increment(n.value)
}
function minus() {
countStore.decrement(n.value)
// 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>
3, src/components/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 setup lang="ts" name="LoveTalk">
import {useTalkStore} from '@/store/loveTalk.ts'
import { storeToRefs } from 'pinia';
const talkStore = useTalkStore()
const { talkList } = storeToRefs(talkStore)
talkStore.$subscribe((mutate,state)=>{
console.log('talkStore保存的数据变化了',mutate,state)
// 浏览器的本地存储,持久化数据保存到本地存储中
localStorage.setItem('talkList',JSON.stringify(state.talkList))
})
// talkStore.$subscribe((mutation, state) => {
// if (mutation.type === 'getAddTalk') {
// console.log('数据更新了', state)
// }
// })
// console.log('@',talkStore)
// 数据
// let talkList = reactive([
// {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// ]);
// 方法 发送请求获取一句土味情话
function getLoveTalk() {
talkStore.getAddTalk()
}
//async function getLoveTalk() {
// 发送请求获取一句土味情话 连续两次结构加重命名
// let result = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// 1. let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// console.log(result.data.content);
// 把请求回来的数据字符串,包装成对象,添加到数组中
// let obj = {id: nanoid(), title: result.data.content};
// 2. let obj = {id: nanoid(), title}; // title: title触发对象的简写形式直接写title就可以了
// console.log(obj);
// 添加到数组中
// 3. talkList.unshift(obj); // 添加到数组开头位置
// talkList.push({id: nanoid(), title: result.data.content});// 添加到数组末尾位置
//}
</script>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
4, src/store/dog.ts
import {defineStore} from 'pinia'
import axios from 'axios'
// 1, 组合式API写法 函数写法
import { reactive } from 'vue'
export const useDogStore = defineStore('dog',()=>{
// dogList相当于state, 相当于data
const dogList = reactive(JSON.parse(localStorage.getItem('dogList') as string)||[])
// addDog相当于actions, 相当于methods
async function addDog(){
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
dogList.push(result.data.message)
} catch (error) {
alert(error)
}
}
// 3, 暴露出去的属性, 相当于computed
return {dogList,addDog}
})
// 2, 选项式API写法 配置对象写法
// export const useDogStore = defineStore('dog',{ // 配置对象
// actions:{
// async addDog(){
// // this.dogList.push(url)
// try {
// let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// this.dogList.push(result.data.message)
// } catch (error) {
// alert(error)
// }
// }
// },
// // 真正存储数据的地方
// state:()=>{
// return {
// dogList:JSON.parse(localStorage.getItem('dogList') as string)||[]
// // 2, dogList:JSON.parse(localStorage.getItem('dogList') as string||'[]'),
// // 3, dogList:localStorage.getItem('dogList')?JSON.parse(localStorage.getItem('dogList')!):[]
// // dogList:[
// // 'https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'
// // ]
// }
// }
// })
5, src/store/count.ts
import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{ // 配置对象
// actions里面放置一个一个的动作函数或者方法,用于响应式组件中的"动作(比如要加,要减)" 用来修改数据
actions: {
increment(value:number) { // 修改数据
// 1, console.log('increment被调用了',value)
// 2, console.log(this)
// 如何修改数据 有两种方式修改数据 使用 this.sum 或者使用this.$state.sum 都可以修改数据
// console.log(this.$state.sum) // 8 拿到sum的值 8
// console.log(this.sum) // 8 拿到sum的值 8
// this 是当前的store实例对象,this.sum 就是访问store里面的数据,this.sum++ 就是修改数据
if (this.sum < 10) {
this.sum += value;
}
},
decrement(value:number) {
console.log('decrement被调用了',value)
if (this.sum > 0) {
this.sum -= value;
}
},
reset() {
// 重置数据
this.sum = 0;
this.school = '北京大学';
}
},
// 真正存储数据的地方
state:()=>{
return {
sum: 6,
school: 'atguigu',
address: '宏福科技园'
}
},
getters: {
// 计算属性
bigSum:state => state.sum * 10,
// bigSum(state) {
// return state.sum * 10; // 计算属性,返回一个新的值
// },
upperSchool():string {
console.log('@@@@',this);
// return state.school.toUpperCase();
return this.school.toUpperCase(); // 计算属性,返回一个新的值
},
// 计算属性,返回一个新的值
// ...... 其他的计算属性
}
})
6, src/store/loveTalk.ts
import {defineStore} from 'pinia'
import axios from 'axios';
import { nanoid } from 'nanoid';
// 1, 组合式API写法 函数写法
import { reactive } from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList相当于state,相当于组件的data属性
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getAddTalk()函数相当于action
async function getAddTalk() {
let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
let obj = {id: nanoid(), title}; // 生成一个唯一的ID,用于唯一标识对话内容
talkList.unshift(obj); // 添加到数组开头位置,实现最新对话在最上方显示的效果
}
return {talkList, getAddTalk}
})
// 2, 选项式API写法 配置对象写法
// export const useTalkStore = defineStore('talk',{ // 配置对象
// actions:{
// async getAddTalk() {
// let {data: {content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json&charset=utf-8')
// let obj = {id: nanoid(), title}; // 生成一个唯一的ID,用于唯一标识对话内容
// this.talkList.unshift(obj); // 添加到数组开头位置,实现最新对话在最上方显示的效果
// }
// // // 添加一个对话
// // addTalk(title:string){ // 添加对话
// // const talk = {id: Date.now().toString(), title}
// // this.talkList.push(talk)
// // },
// // // 删除对话
// // delTalk(id:string){ // 根据ID删除对话
// // const index = this.talkList.findIndex((item)=> item.id === id)
// // if(index !== -1) {
// // this.talkList.splice(index, 1)
// // }
// // },
// // // 清空对话
// // clearTalk(){ // 清空所有对话
// // this.talkList = []
// // }
// },
// // 真正存储数据的地方
// state:()=>{
// return {
// talkList:JSON.parse(localStorage.getItem('talkList') as string) || []
// // 2, talkList:JSON.parse(localStorage.getItem('talkList') as string || '[]')
// // 3, talkList:localStorage.getItem('talkList') ? JSON.parse(localStorage.getItem('talkList')!) : []
// // talkList:[
// // {id: 'zg01', title: '今天你有点怪,哪里怪,怪好看的'},
// // {id: 'zg02', title: '草莓,蓝莓,樱桃,今天想我没有'},
// // {id: 'zg03', title: '心里给你留了一块地,想放点啥就放点啥'},
// // ]
// }
// }
// })
