Skip to content

❤pinia的使用

先来看看pinia的标志

image.png

1、Pinia简介

Pinia官网:https://pinia.vuejs.org/

Pinia起始于 2019 年 11 月左右的一次实验,目的是设计一个拥有组合式 API 的 Vue 状态管理库。

认识Pinia

Pinia是什么:

Pinia 是一个为 Vue 3 设计的状态管理库,vue3建议使用pinia代替vuex进行状态管理。

Pinia旨在提供简洁、强大且易于使用的 API,用于在 Vue 应用程序中管理状态。它提供了一种基于 Vue 3 的响应式系统的方式来管理全局和局部的状态,同时也与 TypeScript 很好地集成在一起。

vue2的都知道vuex状态管理,所谓状态管理,简单来说就是一个存储数据的地方,存放在Vuex中的数据在各个组件中都能访问到,它是Vue生态中重要的组成部分。

而pinia同理也是起到状态管理的作用,但是它又不完全同于vuex,相比有如下优点:

  • Vue2和Vue3都支持,这让我们能很快上手
  • pinia中只有state、getter、action,抛弃了Vuex中的Mutation,Vuex中mutation一直都不太受待见,pinia直接抛弃。
  • pinia中action支持同步和异步
  • 良好的Typescript支持,Vue3推荐使用TS来编写
  • 无需再创建各个模块嵌套了,Vuex中如果数据过多,我们通常分模块来进行管理,而pinia中每个store都是独立的,互相不影响。
  • 体积非常小,只有1KB左右。
  • pinia支持插件来扩展自身功能。
  • 支持服务端渲染。

🌂 理念上我觉得是对组合式API的扩展和衍生:

组合式API(Composable API)是一种设计和构建API的方式,思想就是让不同的API端的点和功能可以像积木一样组合,实现更复杂和定制化的功能。主要强调的其实是模块化、复用性、灵活性。

组合式API核心思想:

JS
1. **模块化设计**:API被设计成独立的、可复用的模块,每个模块提供特定的功能或资源。低耦合度。
2. **高内聚低耦合**:每个API模块分管不同单一性功能,高度内聚。
3. **灵活组合**:开发者可以根据需求自由组合模块构建出新功能。,这种组合可以是静态的(编写代码时确定)或者动态的(运行时确定)。
4. **标准接口**:定义一些标准化的接口和协议确保模块顺利组合。这些标准化接口使模块之间不需要了解对方的内部实现细节。
5. **扩展性和可维护性**:由于组合式API的模块化设计,添加新功能或修改现有功能风险更小。提高了系统的可扩展性和可维护性。

Pinia特点

  • 基于 Vue 3 的响应式系统:Pinia 利用了 Vue 3 的响应式系统,使得在应用中管理状态变得非常直观和高效。
  • 使用 Vue Composition API:Pinia 鼓励开发者使用 Vue 3 的 Composition API 来定义状态和逻辑,这使得代码更清晰和可维护。
  • 零依赖:Pinia 是一个轻量级的库,不依赖于其他状态管理库或类似的工具,使得它具有很高的灵活性。
  • 支持 TypeScript:Pinia 提供了对 TypeScript 的内置支持,包括类型推导、接口定义和类型安全等功能,这使得使用 TypeScript 进行开发变得更加顺畅。
  • 插件系统:Pinia 提供了插件系统,使得开发者可以根据项目的需要进行功能扩展和定制,例如增加中间件、开发工具等。

2、安装使用

安装

js
yarn add pinia

3、Vue3使用pinia

(1)store案例

创建一个 store,例如 counterStore

js
// src/stores/counterStore.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
  },
});

在需要使用 store 的组件中导入并使用它

js
<template>
  <div>
    <p>Count: {{ counterStore.count }}</p>
    <button @click="counterStore.increment()">Increment</button>
    <button @click="counterStore.decrement()">Decrement</button>
  </div>
</template>

<script>
import { useCounterStore } from '@/stores/counterStore';

export default {
  setup() {
    const counterStore = useCounterStore();

    return { counterStore };
  },
};
</script>

到这里我们就可以先去搭建我们的注册登录等模块了,等搭建好了再来进行我们的

接下来我们就在开源项目Nexus之中把这部分的pinia使用尝试一下,并且利用pinia实现我们的登录部分

首先我们获取一下用户的权限,这部分我们需要用到一个东西,这个东西就是js-cookie,拿来存储我们的cookie信息

👉 在 utils=> auth.js ,然后我们使用这个部分把token都放入Cookies部分

js
import Cookies from 'js-cookie'

const TokenKey = 'Admin-Token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

(3)项目使用

👉 新建src = > store => index.js

这里主要导出我们的store

js
const store = createPinia()
export default store

👉 新建store=> modules=> user.js

在store之中我们简单封装一下user的信息,用来存储关于用户的token信息,并且简单的封装一下我们的用户登录信息

这里我们主要是对于用户的账号密码进行传递,然后传递我们的token,默认带一张我们默认的头像部分

js
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import defAva from '@/assets/images/defaulte_avatar.png'

const useUserStore = defineStore(
  'user',
  {
    state: () => ({
      token: getToken(),
      name: '',
      avatar: '',
      roles: [],
      permissions: []
    }),
    actions: {
      // 登录
      login(userInfo) {
        const username = userInfo.username.trim()
        const password = userInfo.password
        const code = userInfo.code
        const uuid = userInfo.uuid
        return new Promise((resolve, reject) => {
          login(username, password, code, uuid).then(res => {
            setToken(res.token)
            this.token = res.token
            resolve()
          }).catch(error => {
            reject(error)
          })
        })
      },
      // 获取用户信息
      getInfo() {
        return new Promise((resolve, reject) => {
          getInfo().then(res => {
            const user = res.user
            const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;

            if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
              this.roles = res.roles
              this.permissions = res.permissions
            } else {
              this.roles = ['ROLE_DEFAULT']
            }
            this.name = user.userName
            this.avatar = avatar;
            resolve(res)
          }).catch(error => {
            reject(error)
          })
        })
      },
      // 退出系统
      logOut() {
        return new Promise((resolve, reject) => {
          logout(this.token).then(() => {
            this.token = ''
            this.roles = []
            this.permissions = []
            removeToken()
            resolve()
          }).catch(error => {
            reject(error)
          })
        })
      }
    }
  })

export default useUserStore

👉 使用我们的用户信息

js

<script setup>
  import useUserStore from '@/store/modules/user'
  const userStore = useUserStore()
  console.log(userStore,'userStore信息');
</script>

测试一下,效果如下,可以继续完善了

image.png

接下来完善我们的登录部分

js
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()

// 处理表单提交的函数
const handleSubmit = async (event) => {
        event.preventDefault();
        // 在实际应用中,这里可以发送注册请求到服务器进行用户注册
        // 这里简单地假设密码和确认密码相同才能注册成功

        if (form.value.username === '' || form.value.password === '') {
            ElMessage.error('用户名和密码不能为空');
            return;
        }else{
            console.log(form.value, 'form.value');
            try {
                const res:any = await login(form.value);
                if(res.code==200){
                    ElMessage.success(res.message);

                     // 调用action的登录方法
                      userStore.login(loginForm.value).then(() => {
                        const query = route.query;
                        const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
                          if (cur !== "redirect") {
                            acc[cur] = query[cur];
                          }
                          return acc;
                        }, {});
                        router.push({ path: redirect.value || "/", query: otherQueryParams });
                      }).catch(() => {
                        loading.value = false;
                      });
                }else{
                   ElMessage.error(res.message);
                }
            } catch (error) {
                // console.log('获取数据详情失败,请重试!',error);
            } finally {
                // console.log('完!');
            }
        }
        return;
};

这里看看我们的登录接口部分

js
import request from '@/utils/request.js'
// 登录方法
export function login(username, password, code, uuid) {
  const data = {
    username,
    password,
    code,
    uuid
  }
  return request({
    url: '/api/login',
    headers: {
      isToken: false,
      repeatSubmit: false
    },
    method: 'post',
    data: data
  })
}

尝试调用一下看看

image.png

这里记得完善一下我们token部分

js
import { getToken } from '@/utils/auth'

权限部分记得更改一下
if (whiteList.indexOf(to.path) !== -1||getToken()) {
    console.log('白名单或者token账号进入1!');
    next();
}

点击登录ok!

image.png

Released under the MIT License.