【 Vue 拓展 | (一) 】:一个案例搞懂 pinia 的使用

一个案例搞懂 pinia 基本使用

项目代码:GitHub 分享链接
第一次尝试以构建案例的方式写博客,从构思大纲到构建项目花了很多时间,希望能够帮助到大家,如果博客对你有帮助请留下你的点赞和关注叭!❤️❤️❤️
请添加图片描述

01. Pinia 基本介绍

符合直觉的 Vue.js 状态管理库

在学习之前,先来了解什么是 pinia 以及为什么要使用 pinia 👇

比如说这是一个项目的基础结构,当组件很少的时候可以通过组件通信来解决,但当组件多了起来

这样再去实现组件通信就显得尤其困难,所以需要一个 可以替我们 集中式 存放很多组件中需要共享的数据(状态),这就是 pinia 的官方介绍 符合直觉的 Vue.js 状态管理库,比如常见的用户登录数据、购物车数据都属于多组件共享的数据,需要一个地方集中式的存储和读取等等。

pinia 相比于 另一个状态管理库 vuex 在使用上最大的特点就是符合直觉,在 pinia 中可以做到 所见即所得,拿取的数据就可以进行直接修改,而不需要再去定义复杂的 mutations 或者 actions 去调用数据,接下来在学习中去感受它的特性。

02. 案例 —— 用户状态管理

注意:为了尽量覆盖所有的知识点,案例中可能存在一些不合理的地方,不适于在真正的环境中使用。

2.1 启动项目

👉 创建 vue3 项目

$ npm create vue@latest

$ pnpm create vue@latest

$ yarn create vue@latest

$ bun create vue@latest

按照本图配置即可,然后我们使用 WebStrom 打开项目(VsCode 等均可)

👉 安装依赖

$ npm install

👉 打开 package.json 运行 “dev” 确保项目可以正确启动

注意 💡:

因为这里是使用 vite 构建项目,且预先选择了 pinia 引入,如果是已有的项目请按照如下的方式:

👉 安装

$ yarn add pinia
# 或者使用 npm
$ npm install pinia

👉 创建实例并且挂载到 app

import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)

app.use(createPinia())

app.mount('#app')

2.2 案例准备

👉 清除默认的样式

  • 删除 App.vue 中的填充部分
  • 删除 main.ts 中的 import './assets/main.css'

👉 引入 Arco Design 组件库:为了方便开发这里引入现有的组件库

  • 安装

    # npm
    $ npm install --save-dev @arco-design/web-vue
    # yarn
    $ yarn add --dev @arco-design/web-vue
    
  • 全局引入

    import { createApp } from 'vue'
    import ArcoVue from '@arco-design/web-vue'
    import App from './App.vue'
    import '@arco-design/web-vue/dist/arco.css'
    
    const app = createApp(App);
    app.use(ArcoVue);
    app.mount('#app');
    

💡 什么是组件库?

​ 前端组件库是一组可重复使用的前端组件和样式,旨在简化Web开发过程,提高开发效率和一致性。这些组件通常包括按钮、表单、导航栏、模态框、图表等,可以轻松集成到项目中,使开发人员能够快速构建用户界面。

👉 构建界面

👉 按照这个结构搭建一个基础的架子,数据使用假数据填充

最终结果:

源码:

<template>
  <div class="userMessage">
    <div class="showAvatar">
      <div class="avatar"></div>
      <div class="userName">*Soo_Young*</div>
    </div>

    <div class="basicMessage">
      <a-descriptions style="margin-top: 20px" :data="data" :size="size" title="基本信息" :column="1"/>
    </div>
  </div>
</template>
<script setup lang="ts">
import {ref} from "vue";

const birthday = new Date(2004, 8, 14);
const formatDate = () : string => {
  let year = birthday.getFullYear();
  let month = birthday.getMonth();
  let day = birthday.getDate();
  let monthStr;
  let dayStr;
// 格式化月份和日期为两位数
  if (month < 10) {
    monthStr = '0' + month.toString();
  } else {
    monthStr = month;
  }
  if (day < 10) {
    dayStr = '0' + day.toString();
  } else {
    dayStr = day;
  }

  return year + '-' + monthStr + '-' + dayStr;
}
const data = ref([{
  label: '姓名',
  value: '*Soo_Young*'
}, {
  label: '电话',
  value: '123-1234-1234',
}, {
  label: '住址',
  value: '北京'
}, {
  label: '生日',
  value: formatDate(),
},])
const size = "large";
</script>
<style scoped>
.userMessage {
  display: flex;
  flex-direction: column;
  background-color: #f5f6f7;
}
.showAvatar {
  background-color: #ffffff;
  margin-top: 50px;
  display: flex;
  width: 1500px;
  height: 100px;
  padding-left: 30px;
}
.showAvatar .avatar {
  width: 80px;
  height: 80px;
  border-radius: 80px;
  background-image: url("https://thirdwx.qlogo.cn/mmopen/vi_32/ibO6F2GNoPIxP9ibg7CQNymDUYZegTLFILic1KM6NmUYMXHKKNZBnFd7dpPrLnvxZTVUibicqBov4vib7e0mfGah0SGw/132");
  background-repeat: no-repeat;
  background-size: cover;
}
.showAvatar .userName {
  margin-top: 40px;
  margin-left: 30px;
  font-weight: 700;
}
.basicMessage {
  background-color: #ffffff;
  margin-top: 20px;
  padding: 50px;
}
</style>

2.3 需求分析

先来明确一下这个案例的需求

  1. 实现从全局库中拿取数据并且展示
  2. 实现用户名的修改
  3. 新增计算生日,用户查询今天距离自己生日还有几天
  4. 新增修改用户信息界面,并且将数据存储到 localStage 中实现持久化存储

因为需求都比较简单,这里就不做过多赘述了,下面开始开发。

2.4 从全局库中拿取数据并且展示和修改

🏠 在存储数据之前,先需要一个仓库,所以这里先来创建一个全局的状态库,Store 是一个保存:状态业务逻辑 的实体,每个组件都可以读取写入它。

  • 它有三个概念:stategetteraction,相当于组件中的: datacomputedmethods;这里先使用其中的 state 部分。

💡 工程化建议

  1. 将库相关的代码放到 /store 文件夹下
  2. 文件命名为所存储的数据比如 /user.ts

👉 在 /store 目录下创建 user.ts

💡 这里模拟的是已经向后端请求到用户数据的情况,正常应该是向后端请求信息,且比较重要的信息是不会明文存储的。

import { defineStore } from "pinia"
export const useUserStore = defineStore('counter', {
  state: () => ({
      
  }),
})

state 中的数据就是全局库中存储的数据,接下来,将上面的模拟数据加到 state 中:

// 定义并暴露一个store
export const useUserStore = defineStore('counter', {
    state: () => ({
        name: "*Soo_Young*",
        phone: "123-1234-1234",
        address: "北京",
        birthday: new Date(2004, 8, 14)
    }),
})

👉 返回 App.vue 组件来使用其中存储的数据

// 引入 store 并且使用
import {useUserStore} from "@/stores/user";
const userStore = useUserStore();
// 直接拿取数据
const birthday = userStore.birthday;
const data = ref([{
  label: '姓名',
  value: userStore.name
}, {
  label: '电话',
  value: userStore.phone,
}, {
  label: '住址',
  value: userStore.address
}, {
  label: '生日',
  value: formatDate(),
},])

💡 经过这样的处理,展示效果和之前完全相同,不知道大家有没有体会到其 符合直觉 的特点,拿取数据就可以直接使用。

👉 使用解构来简化调用

  • 为了避免重复写 userStore. 前缀,这里可以使用解构将其解构为新的 ref 对象,但注意,直接解构会使得数据失去响应式,而使用 toRefs API 又会导致其中所有的部分都被结构为 ref 对象,所以这里使用新的 API:storeToRefs 它只会将数据部分转为 ref 对象。
const userStore = useUserStore();
const {name, birthday, phone, address} = storeToRefs(userStore);


// 再使用就需要添加 value
let year = birthday.value.getFullYear();
let month = birthday.value.getMonth();
let day = birthday.value.getDate();

2.5 修改用户名

👉 实现用户名的修改

  • 先来构建一个表单界面,使用 v-model 实现双向绑定

    <div class="changeName">
      更改用户名:<input type="text" v-model="newName"><br><br>
      <a-button type="primary" @click="changeName">提交</a-button>
    </div>
    
    const newName = ref(); // 用户的新用户名
    const changeName = () => {
      name.value = newName.value;
      console.log(name.value);
    }
    
    .changeName {
      margin-top: 20px;
      background-color: white;
      padding: 50px;
    }
    

    当点击提交的时候会调用这个方法来赋值给库中的数据最终实现数据的修改

💡 这种直接修改的方式在 vuex 中无法想象的,显然这样的调用更加的方便和符合直觉。

👉 修改数据的三种方式

除了上面的直接修改,还有两种修改方式可以实现修改

  1. 直接修改,注意下面两种方式 不要混用

    # 直接使用
    userStore.name = Kk
    
    # 解构
    name.value = Kk
    
  2. 批量修改:可以使用一条语句一次修改多个数据

    userStore.$patch({
      name:Kkq,
      phome:'122-132-122'
    })
    
  3. 构建 action 方法来修改:上面提到 action 其实就类似于 method

    export const useUserStore = defineStore('counter', {
        actions: {
            changeName() {
                this.name = "Kkq";
            },
        },
        state: () => ({
            name: "*Soo_Young*",
            phone: "123-1234-1234",
            address: "北京",
            birthday: new Date(2004, 8, 14)
        }),
    })
    

    接下来在 @click 后直接调用这个方法,就可以实现修改

    <a-button type="primary" @click="userStore.changeName()">提交</a-button>
    

2.6 查看距离生日的日期

💡 计算如果写在组件中会显得比较冗余,而这种对于数据的 拓展 就可以使用计算属性来书写,也就是上面提到的 getters

👉 书写计算生日的 getters

    getters: {
        dayToBirth: (state) => {
            let birthday = new Date(state.birthday);
            let today = new Date();
            birthday.setFullYear(today.getFullYear());
            if (today > birthday) {
                birthday.setFullYear(today.getFullYear() + 1);
            }
            let diffInMilliseconds:number = Number(birthday) - Number(today);
            return Math.ceil(diffInMilliseconds / (1000 * 60 * 60 * 24));
        },
    },

💡 在 getters 中数据,是一个方法,其返回值就是得到的数据,调用时会传入值 state,可以直接从 state 中去拿取数据和修改。

👉 调用数据

  • basicMessage 盒子中去使用上面的数据即可实现效果

        <div class="basicMessage">
          <a-descriptions style="margin-top: 20px" :data="data" :size="size" title="基本信息" :column="1"/>
          <div class="dayToBirth">
            距离您的生日还有 {{userStore.dayToBirth}}</div>
        </div>
    

2.7 实现用户名的本地持久化存储

💡 上面的用户名受限于 pinia 刷新即丢失的限制导致刷新页面还是会读取原来的数据,这里可以使用 localStage 来持久化存储数据;所以需要一个方式来监听姓名的修改,当用户修改后就将信息存储到 localStage 中,且刷新后再读取也是读取 localStage 中的值。

👉 这就需要提供监听能力的方法,来监听 store 中的数据的变化,当其变化后就做出修改:

userStore.$subscribe((mutate, state) => {
  localStorage.setItem('userName', JSON.stringify(name.value));
})

💡 在Pinia中,userStore.$subscribe()方法用于订阅Store的状态变化,并在状态变化时执行回调函数。回调函数接收两个参数:

  1. mutate: 这是一个函数,用于提交mutation以更新Store的状态。通过调用mutate函数,你可以在订阅状态变化时进行一些逻辑处理,例如更新Store中的数据。
  2. state: 这是当前Store的状态对象。你可以通过state参数访问Store中的状态,并在订阅状态变化时对状态进行操作或检查状态的变化。

这里暂时没有用到这两个参数

👉 实现刷新后读取

  • 这时候 store 中获取数据就可以直接从 localStage 中拿取了

        state: () => ({
            name: JSON.parse(localStorage.getItem('userName') as string) || 'Kkq',
            phone: "123-1234-1234",
            address: "北京",
            birthday: new Date(2004, 8, 14)
        }),
    

💡 使用 JSON.parse() 将其转为正常结构,再通过 || 提供为 null 时的默认值

这样即使当页面刷新数据也不会丢失了。

03. 常用步骤总结

👉 创建项目

$ npm create vue@latest

$ pnpm create vue@latest

$ yarn create vue@latest

$ bun create vue@latest

👉 引入 pinia 依赖

$ yarn add pinia
# 或者使用 npm
$ npm install pinia

👉 挂载到 main.ts

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')

👉 定义 store

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

👉 解构

const userStore = useUserStore();
const {name, birthday, phone, address} = storeToRefs(userStore);


// 再使用就需要添加 value
let year = birthday.value.getFullYear();
let month = birthday.value.getMonth();
let day = birthday.value.getDate();

👉 监听修改

userStore.$subscribe((mutate, state) => {
  localStorage.setItem('userName', JSON.stringify(name.value));
})
  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*Soo_Young*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值