Vue3研学-Pinia(二)

三 变得优雅

1 storeToRefs

toRefs 用于解构普通 reactive 对象保持响应性,而 storeToRefs 是 Pinia 专用工具,专为解构 Store 的 state 设计(只关注数据,不会将方法ref包裹),二者适用场景不同,推荐按需选用以确保响应性。

① Count.vue

ts 复制代码
<template>
  <div class="count">
    <h2>开始计算:{{sum}}</h2>
    <h3>{{nikename}}出了新时装{{fashion}}</h3>
    <select v-model.number="n">
      <!-- 或用:value -->
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="add">+</button>
    <button @click="subtract">-</button>
  </div>
</template>

<script setup lang="ts" name="Count">
  import {ref,reactive,toRefs} from "vue";
  // 引入 storeToRefs
  import {storeToRefs} from "pinia";
  // 引入刚才的仓库
  import {useCountStore} from '@/store/count';

  const countStore = useCountStore()

  let n = ref(1) // 选择的数字

  const {sum,nikename,fashion} = toRefs(countStore)
  console.log('@@@',toRefs(countStore))
  console.log('!!!',storeToRefs(countStore))

  // 方法
  function add(){
    // 方法3
    countStore.increment(n.value)
  }
  function subtract(){
    countStore.sum -= n.value
  }
</script>

<style scoped>
.count {
  font-family: "游明朝", "Yu Mincho", serif;
  background: #f5e8d0; /* 米色背景 */
  border: 2px solid #8b0000; /* 深红色边框 */
  border-radius: 8px;
  padding: 1rem;
  box-shadow: 4px 4px 0 #5c3317; /* 复古阴影 */
  text-align: center;
}

.count h2 {
  color: #8b0000; /* 深红色标题 */
  font-size: 1.5rem;
  text-shadow: 1px 1px 0 #f5e8d0; /* 轻微描边增强可读性 */
  margin: 0;
  padding: 0.5rem;
  border-bottom: 1px dashed #5c3317; /* 虚线分隔线 */
}

select, button {
  font-family: "游明朝", serif; /* 昭和风字体 */
  background: #f5e8d0; /* 米色背景 */
  color: #8b0000;     /* 深红文字 */
  border: 1px solid #5c3317; /* 深棕边框 */
  padding: 0.4rem 0.8rem;
  box-shadow: 2px 2px #5c3317; /* 复古阴影(省略模糊半径) */
  cursor: pointer;
}

select:hover, button:hover {
  background: #e8d5b5; /* 悬停加深背景 */
}

button:active {
  box-shadow: 1px 1px #5c3317; /* 点击时阴影收缩 */
  transform: translateY(1px); /* 轻微下移 */
}
</style>

② RapTalk.vue

ts 复制代码
<template>
  <div class="talk">
    <button @click="getRapTalk">用最潮的方式来表达爱</button>
    <ul>
      <li v-for="talk in talkList" :key="talk.id">{{ talk.title }}</li>
    </ul>
  </div>
</template>

<script setup lang="ts" name="RapTalk">
  import {storeToRefs} from "pinia";
  import {useTalkStore} from '@/store/rapTalk';

  const talkStore = useTalkStore()

  const {talkList} = storeToRefs(talkStore);

  // 方法
  async function getRapTalk(){
    talkStore.getTalk()
  }
</script>

<style scoped>
.talk {
  font-family: 'Yomogi', cursive;
  background: #f9f0e1;
  border: 1px solid #8b0000;
  padding: 12px;
}

button {
  background: #8b0000;
  color: white;
  border: none;
  padding: 4px 12px;
  cursor: pointer;
}

li {
  margin: 8px 0;
  padding: 8px;
  background: #fffaf0;
  border-left: 2px solid #d2b48c;
}
</style>

2 getters

① 介绍

getters 是 计算属性(Computed Properties),用于从 state 中派生出新的数据或逻辑。它的核心作用是 封装复杂计算、提高代码复用性、保持响应式更新,类似于 Vue 组件中的 computed,但作用域是 Store 级别(跨组件共享)。

② count.ts

ts 复制代码
import {defineStore} from "pinia";

export const useCountStore = defineStore('count',{
    // 搭配方法3使用,存储函数用以响应组件中的动作
    actions:{
        // 增加动作 可引入逻辑判断
        increment(value:number){
            // 修改数据
            console.log('@@@',this.sum)
            if (this.sum < 3366){
                this.sum += value
            }
        }
    },
    state(){
        return{
            sum:888,
            nikename:'雕刻家',
            fashion:'笼中偶'
        }
    },
    getters:{
        // 类似计算属性
        bestSum:state => state.sum * 10,
        // bestSum(state){
        //     return state.sum * 10
        // },
        //
        upFashion():string{
            return this.fashion.concat('PLUS')
        }
    }
})

③ Count.vue

ts 复制代码
<template>
  <div class="count">
    <h2>开始计算:{{sum}}*10后:{{bestSum}}</h2>
    <h3>{{nikename}}出了新时装{{fashion}}升级{{upFashion}}</h3>
    <select v-model.number="n">
      <!-- 或用:value -->
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="add">+</button>
    <button @click="subtract">-</button>
  </div>
</template>

<script setup lang="ts" name="Count">
  import {ref,reactive,toRefs} from "vue";
  // 引入 storeToRefs
  import {storeToRefs} from "pinia";
  // 引入刚才的仓库
  import {useCountStore} from '@/store/count';

  const countStore = useCountStore()

  let n = ref(1) // 选择的数字

  // 取用 bestSum,upFashion
  const {sum,nikename,fashion,bestSum,upFashion} = toRefs(countStore)
  console.log('@@@',toRefs(countStore))
  console.log('!!!',storeToRefs(countStore))

  // 方法
  function add(){
    // 方法3
    countStore.increment(n.value)
  }
  function subtract(){
    countStore.sum -= n.value
  }
</script>

<style scoped>
.count {
  font-family: "游明朝", "Yu Mincho", serif;
  background: #f5e8d0; /* 米色背景 */
  border: 2px solid #8b0000; /* 深红色边框 */
  border-radius: 8px;
  padding: 1rem;
  box-shadow: 4px 4px 0 #5c3317; /* 复古阴影 */
  text-align: center;
}

.count h2 {
  color: #8b0000; /* 深红色标题 */
  font-size: 1.5rem;
  text-shadow: 1px 1px 0 #f5e8d0; /* 轻微描边增强可读性 */
  margin: 0;
  padding: 0.5rem;
  border-bottom: 1px dashed #5c3317; /* 虚线分隔线 */
}

select, button {
  font-family: "游明朝", serif; /* 昭和风字体 */
  background: #f5e8d0; /* 米色背景 */
  color: #8b0000;     /* 深红文字 */
  border: 1px solid #5c3317; /* 深棕边框 */
  padding: 0.4rem 0.8rem;
  box-shadow: 2px 2px #5c3317; /* 复古阴影(省略模糊半径) */
  cursor: pointer;
}

select:hover, button:hover {
  background: #e8d5b5; /* 悬停加深背景 */
}

button:active {
  box-shadow: 1px 1px #5c3317; /* 点击时阴影收缩 */
  transform: translateY(1px); /* 轻微下移 */
}
</style>

3 $subscribe

① 介绍

$subscribe() 是 Store 实例提供的一个方法,用于 监听整个 Store 的 state 变化,并在变化时触发回调函数。它的核心作用是 响应式地跟踪状态变更,类似于 Vue 的 watch(),但更专注于 Store 的全局状态管理。

② rapTalk.ts

从 localStorage 获取 'talkList' 数据时,需通过 JSON.parse 解析字符串并处理可能的 null 或解析错误

ts 复制代码
import {defineStore} from "pinia";
import axios from "axios";
import {nanoid} from "nanoid";

export const useTalkStore = defineStore('talk',{
    actions:{
        async getTalk(){
            let {data:{content:title}} = await axios.get('https://v1.jinrishici.com/rensheng.json');
            let obj = {id:nanoid(),title}
            this.talkList.unshift(obj)
        }
    },
    state(){
        return{
            // 获取本地存储内容,注意存入的字符串需转回来才可用
            // 通过断言强制将返回值视为 string 类型。但存在风险:如果 'talkList' 不存在或不是字符串(如 null),后续解析会报错。
            // 提供默认值 [](空数组)以防解析失败或数据不存在。
            talkList:JSON.parse(localStorage.getItem('talkList') as string)||[]
        }
    }
})

③ RapTalk.vue

可将数组内容存入本地存储,实现刷新不丢失。

ts 复制代码
<template>
  <div class="talk">
    <button @click="getRapTalk">用最潮的方式来表达爱</button>
    <ul>
      <li v-for="talk in talkList" :key="talk.id">{{ talk.title }}</li>
    </ul>
  </div>
</template>

<script setup lang="ts" name="RapTalk">
  import {storeToRefs} from "pinia";
  import {useTalkStore} from '@/store/rapTalk';

  const talkStore = useTalkStore()
  const {talkList} = storeToRefs(talkStore);
  // 类似 watch 参数为:本次修改信息mutate 数据state
  talkStore.$subscribe((mutate,state)=>{
    console.log('talkStore内数据变化了',mutate,state)
    // 将talkList数组内容作为字符串形式存入本地
    localStorage.setItem('talkList',JSON.stringify(state.talkList))
  })

  // 方法
  async function getRapTalk(){
    talkStore.getTalk()
  }
</script>

<style scoped>
.talk {
  font-family: 'Yomogi', cursive;
  background: #f9f0e1;
  border: 1px solid #8b0000;
  padding: 12px;
}

button {
  background: #8b0000;
  color: white;
  border: none;
  padding: 4px 12px;
  cursor: pointer;
}

li {
  margin: 8px 0;
  padding: 8px;
  background: #fffaf0;
  border-left: 2px solid #d2b48c;
}
</style>


删除本地存储可重新添加歌词

4 store组合式写法

① 介绍:将选项式写法改为组合式写法,RapTalk.vue不需改动

② rapTalk.ts

ts 复制代码
import {defineStore} from "pinia";
import axios from "axios";
import {nanoid} from "nanoid";
// 引入 reactive
import {reactive} from "vue";

// 选项式改组合式写法   参数二 改为函数
export const useTalkStore = defineStore('talk',()=>{
    // talkList => state
    const talkList=reactive(
        JSON.parse(localStorage.getItem('talkList') as string)||[]
    )
    // getTalk => actions
    async function getTalk(){
        let {data:{content:title}} = await axios.get('https://v1.jinrishici.com/rensheng.json');
        let obj = {id:nanoid(),title}
        talkList.unshift(obj)
    }
    // 组合式要注意返回
    return {talkList,getTalk}
})
相关推荐
lsx2024063 小时前
FastAPI 交互式 API 文档
开发语言
VCR__3 小时前
python第三次作业
开发语言·python
码农水水3 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展
wkd_0073 小时前
【Qt | QTableWidget】QTableWidget 类的详细解析与代码实践
开发语言·qt·qtablewidget·qt5.12.12·qt表格
C澒3 小时前
面单打印服务的监控检查事项
前端·后端·安全·运维开发·交通物流
东东5163 小时前
高校智能排课系统 (ssm+vue)
java·开发语言
余瑜鱼鱼鱼3 小时前
HashTable, HashMap, ConcurrentHashMap 之间的区别
java·开发语言
m0_736919103 小时前
模板编译期图算法
开发语言·c++·算法
【心态好不摆烂】3 小时前
C++入门基础:从 “这是啥?” 到 “好像有点懂了”
开发语言·c++
pas1363 小时前
39-mini-vue 实现解析 text 功能
前端·javascript·vue.js