Vue3(四):Pinia

一、Pinia介绍

Pinia是一个专门为Vue.js设计的状态管理库,它提供了一种简单和直观的方式来管理应用程序的状态。在使用Pinia时,可以轻松地创建定义状态的存储,然后将其与Vue组件绑定,使它们能够使用该状态。和上一个博客提到的Vuex相比,Pinia 更加简单易用,体积更小,同时具有更好的 TypeScript 支持和插件系统。

在Vue.js的官网中,我们可以看到Pinia目前已经取代Vuex,成为Vue生态系统的一部分。

Vue2中我们用的是vuex,在Vue3中我们用Pinia.下面是vuex和pinia对比:

mutations 不再存在。他们经常被认为是 非常 冗长。他们最初带来了 devtools 集成,但这不再是问题。
无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
不再需要注入、导入函数、调用函数、享受自动完成功能!
无需动态添加 Store,默认情况下它们都是动态的,您甚至都不会注意到。请注意,您仍然可以随时手动使用 Store 进行注册,但因为它是自动的,您无需担心。
不再有 modules 的嵌套结构。您仍然可以通过在另一个 Store 中导入和 使用 来隐式嵌套 Store,但 Pinia 通过设计提供平面结构,同时仍然支持 Store 之间的交叉组合方式。 您甚至可以拥有 Store 的循环依赖关系。
没有 命名空间模块。鉴于 Store 的扁平架构,“命名空间” Store 是其定义方式所固有的,您可以说所有 Store 都是命名空间的。

接下来我们准备实现这样一个效果:

二、搭建Pinia 环境

1.通过以下方式安装:

(建议先去看一下字节vue版本,如果是vue3.3以上的可以直接安装2.1版本(默认最新版本)的pinia,如果不是请安装更低版本,否则项目运行时将报错) 

npm install pinia@2.0.36

2.引入并创建使用pinia

//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');

三、准备项目效果前期 

App.vue

<template>
    <Count />
    <br>
    <LoveTalk />
</template>

<script setup lang="ts" name="App">
import Count from './components/Count.vue'
import LoveTalk from './components/LoveTalk.vue'
</script>

components/Count.vue

<template>
    <div class="count">
        <h2>当前求和为:{{ 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 } from "vue";
// 数据
let sum = ref(1) // 当前求和
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>

components/LoveTalk.vue

(这里涉及到一个nanoid库的使用,直接npm i nanoid即可,主要用于生成唯一id)

<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;
    box-shadow: 0 0 10px;
}
</style>

这样我们可以看到初期效果 

 

 四、store和state/存储+读取数据

在src文件夹下新建store文件夹,在store文件夹下新建count.ts和loveTalk.ts。

1.store

Store是 Pinia 中管理状态的核心概念。它相当于一个 Vue 组件中的状态,但是 Store是一个独立的模块。

Store 是用 defineStore() 定义的,它的第一个参数要求是一个独一无二的名字,这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use… 是一个符合组合式函数风格的约定。

以useCountStore为例创建一个store:

// 引入defineStore用于创建store
import { defineStore } from 'pinia';

// 定义并暴露一个store
export const useCountStore = defineStore('count', {
...
})

2.State

State 是 store 中存储数据的地方。可以把之前存储在vue文件中的数据拿过来存储在state里面。通过定义 State,可以在 store 的任何位置访问和修改数据。

在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。

用法:

count.ts和Count.vue中

同理,loveTalk.ts和LoveTalk.vue中

截至到这里就完成了存储和读取数据,但是还没有完成修改的功能,点击按钮是无效的。 

五、修改数据的三种方法

第一种:countStore.

以Count.vue为例,修改sum为例 

function add() {
    // 第一种修改数据的方法
    countStore.sum += 1
}

第一种修改方式就是修改几次mutation几次 

第二种:$patch

通过$patch 批量修改

<h2>当前求和为:{{ countStore.sum }}</h2>
  <h2>学校名称是{{ countStore.school }},坐落于{{ countStore.address }}</h2>
 
function add() {

    // 第二种 批量修改
    countStore.$patch({
        sum:888,
        school:'尚硅谷',
        address:'郑州'
    })
}

第二种通过$patch批量修改,点一次修改一组,提示修改patch一次 

第三种:actions 

适用于大量相同的修改,可以复用 

同理,loveTalk.ts和LoveTalk.vue中

六、storeToRefs 

解释:借助`storeToRefs`将`store`中的数据转为`ref`对象,方便在模板中使用。

注意:`pinia`提供的`storeToRefs`只会将数据做转换,而`Vue`的`toRefs`会转换`store`中数据。

用法:

(1)引入

import { storeToRefs } from "pinia";

(2)使用

// 只会关注数据,不会对方法进行ref包括
const {sum,school,address} = storeToRefs(countStore)

 (3)例如:之前这样写就很冗杂,怎么把countStore去掉呢?

如果直接使用const  {sum,school,address} = countStore.肯定是不行的,会丢失响应式。我们会想到用torefs,但是这里假如用torefs包括的话就会很危险,因为torefs是把所有的数据方法都变成响应式的了。但是我们这里不需要把方法变成响应式,所以就引入了storeToRefs

同理LoveTalk.vue中 

七、getters

1. 概念

当`state`中的数据,需要经过处理后再使用时,可以使用`getters`配置。

2.使用 

有两种方法:

第一种是使用箭头函数 

getters: {
    // 第一种
    // bigSum(state){
    //   return state.sum*10
    // },
    bigSum: (state) => state.sum * 10,
}

这里更正一下,我发现我用老师讲的第一个方法会报错,但用this写就不报错(用this就相当于第二种方法了)

 

第二种是用this 

getters: {
// 第二种 this
    upperSchool():string{
      return this.school.toUpperCase()//变成大写,this相当于store
    }
  },

 这两种方法都要在count.vue中引入,并使用插值语法引用

const {sum,school,address,bigSum,upperSchool} = storeToRefs(countStore)

八、$subscribe()

通过 store 的 `$subscribe()` 方法侦听 `state` 及其变化

在lovetalk.vue中 

talkStore.$subscribe((mutate, state) => {
    console.log('LoveTalk', mutate, state)
    //把数据存储在本地存储防止丢失,
    localStorage.setItem('talk', JSON.stringify(talkList.value))
}),

用stringify是为了把数据转化为字符串存储,否则显示object

如下所示,在本地存储存储成功

 这样我们可以实现刷新不丢失:

在lovetalk.ts中,不要写那些原来的数据,换成以下内容

但是这样还会有一个问题,就是初始的时候没有数据存在, 会报错

可以使用空数组来存初始的

state() {
    return {
      talkList:JSON.parse(localStorage.getItem('talkList') as string) ||[]
    };
  },

 九、store组合写法

以前是这样写的,里面包的actions,state 

组合写法就可以用类似于setup的写法,比较正常,不过最后记得return用到的方法 


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 };
});

 以上仅为个人学习笔记,如有错误请指正

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值