一个登录案例学会 Pinia

2023-10-27

Pinia 号称下一代的 Vuex。

经过初步体验,发现相比于 Vuex,Pinia 确实有了很大进步,最明显的就是删减了复杂的概念,简化了数据流转的过程,现在只剩下了 store、state、getters、actions 这四个核心概念。

接下来使用一个用户登录的案例,来学习 Pinia 的使用。

案例概述

需要用到:

  • vite:创建和管理 vue 项目
  • pinia:状态管理
  • axios:网络请求
  • vite-plugin-mock:提供登录的 mock 接口
  • pinia-plugin-persistedstate:状态持久化插件

我们会先 mock 一个简单的登录接口,然后介绍使用 Pinia 的基本流程,最后在组件中使用 Pinia,完成整个流程。

初始化工程

创建 Vue 项目可以使用 create-vite 和 create-vue 这两个脚手架。案例使用前者。

pnpm create vite pinia-login

选择 Vue 和 使用 JavaScript 构建:

image-20221007164049987

image-20221007164147819

然后进入项目并安装依赖:

cd vite pinia-login
pnpm install

代码示例上传到仓库。到最后完成后的目录结构如下:

image-20221010221819787

Mock 的用法

项目开发阶段通常会使用 mock 数据。插件 vite-plugin-mock 同时提供了开发环境和生产环境下的数据 mock 服务,简单好用。

安装

插件依赖于 mockjs,需要一并安装:

pnpm add -D vite-plugin-mock mockjs

配置

vite.config.js 配置文件启用插件。

Mock 服务通常只用于开发阶段,因此我们需要在配置文件中判断当前所处环境。

在 webpack 中通常会配置一个 NODE_ENV 的环境变量。而在 Vite 中,不用开发者进行设置,它提供了一种方便的判断开发环境和生产环境的方式,如下:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteMockServe } from 'vite-plugin-mock'

export default defineConfig((config) => {
  const { command } = config
  return {
    plugins: [
      vue(),
      viteMockServe({
        // 只在开发阶段开启 mock 服务
        localEnabled: command === 'serve'
      })
    ]
  }
})

上面,配置文件导出一个立即执行的 defineConfig 函数。它又接收一个函数作为参数,该函数接收一个 config 参数,它包含一个 command 属性。当在命令行中执行 vite (开发)命令时, command 的值为 serve,当执行 vite build (构建)命令时,对应的值为 build。据此,可以识别所处环境。

插件 vite-plugin-mock 有一个配置项 localEnabled,可以决定是否开启 mock 服务。默认即为开启状态。结合 command 属性,就可以动态的切换 mock 服务的状态了。

编写 mock server

该插件开箱即用。默认它会读取项目根目录下 mock 文件下的内容,作为 mock server。

新建一个模拟用户接口的服务,它导出一个数组,数组里每一项用来模拟一个接口:

// /mock/user.js

export default [
  // 用户登录
  {
    // 请求地址
    url: "/api/user/login",
    // 请求方法
    method: "post",
    // 响应数据
    response: () => {
      return {
        code: 0,
        message: 'success',
        data: {
          token: "Token",
          username: "昆吾kw"
        }
      }
    }
  }
]

插件内部使用了 Connect 来提供接口服务,它是一个比 Express 更悠久的 Node HTTP 框架。上面的写法就相当于创建了一个这样的接口服务:

app.post('/api/user/login', (req, res)=>{
    res.send({
        code: 0,
        message: 'success',
        data: {
          token: "Token",
          username: "昆吾kw"
        }
    })
})

由于开启了mock 服务,当前端在发出 ajax 请求时,会被拦截到,交由 mock 服务处理。没有做数据校验,前端传任何数据来都返回上面的结果。

使用 Pinia

如果用过 Vuex,那么可以无缝切换到 Pinia,用过之后,你会直呼简约就是美

Pinia 提供了更简单的 API,具有更少的规范,提供了 Composition API 风格的 API。尤其是对 Typescript 的支持,比 Vuex 好用太多。

Pinia 的核心概念只剩下了:

  • store:状态仓库
  • state:状态,和 vuex 保持一致
  • getters:类似组件的计算属性,和 vuex 中的 getters 的保持一致
  • actions:和 vuex 中的 actions 保持一致,可以处理逻辑并修改 state

安装依赖:

pnpm add pinia axios

axios 一会在 actions 中发送请求。

使用 Pinia 的一般套路是:

  1. 创建 pinia

  2. 注册 pinia

  3. 创建 store

  4. 抽离需要管理的数据作为 state,声明 getters 优化状态读取,声明 actions 处理业务逻辑

  5. 在需要的地方(组件或其他),导入和使用 store

创建 pinia

Vuex 的用法一样,通常会在 src 目录下创建 store 目录来存放状态管理有关的代码。

首先是创建 pinia 插件。在该文件中,从安装好的 pinia 模块中导出一个 createPinia 方法,它用于创建一个pinia 件实例供 Vue 注册和使用。

// store/index.js

import { createPinia } from 'pinia'

const pinia = createPinia()

export default pinia

注册 pinia

在项目的入口文件中,注册上面创建出来的 pinia 插件。

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

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

app 实例调用 use 方法来注册插件。在 Vue2 中,注册插件直接调用 Vue.use方法。

创建用户 store

过去使用 Vuex 时,通常会先创建一个根 store,然后划分模块,每个模块拆分成一个文件进行管理,然后再导入根 store 中注册。

Pinia 中没有 module 的概念,是一个拍平的 store 结构。Pinia 推荐按照功能去划分一个个的 store ,这样更方便管理和使用。

使用 defineStore 方法创建 store,store 的命名遵循 useXXX 的形式。创建时需要指定一个唯一的 id,有两种方式:

const useStore = defineStore('main', {
  // other options...
})

const useStore = defineStore({
   id: 'main'
  // other options...
})

下面是定义的用户 store

// store/user.js

import axios from 'axios'
import { defineStore } from 'pinia'

// 创建 store
const useUserStore = defineStore('user', {
  // 定义状态:一个函数,返回一个对象
  state: () => ({
    username: '',
    token: ''
  }),
   
  // 定义 getters,等同于组件的计算属性
  getters: {
    // getter 函数接收 state 作为参数,推荐使用箭头函数
    hello: state => 'Hello!' + state.username
  },
  
  // 定义 actions,有同步和异步两种类型
  actions: {
    // 异步 action,一般用来处理异步逻辑
    async login(userData) {
      const result = await axios.post('/api/user/login', userData)
      const { data, code } = result.data
      if (code === 0) {
        // action 中修改状态
        this.username = data.username
        this.token = data.token
      }
    },

    // 同步 action
    logout() {
      this.token = ''
      this.username = ''
    }
  }
})

export default useUserStore

state、getters、actions

这几个概念,相信大家都很熟悉了,就不再过多介绍了。

重点说下 actions 。过去要修改 store 中的状态,需要先 dispatch action,再 commit mutation,真的很繁琐。

这次最大的改变,就是不再需要 dispatch 了,也没有 mutation 的概念了,可以当作普通函数那样使用就好了。无论是同步逻辑,还是异步逻辑,现在都可以一股脑写在 actions 中了。在一个 action 函数中, this 就是当前 store 的实例,可以直接修改状态。

组件中使用 Pinia

组件中使用 store 非常方便,使用哪个就导入哪个。

PiniaVuex4 一样,支持 Composition API ,先实例化 store;实例化 store 之后,可以直接使用它的 stategettersactions

// App.vue

<script setup>
import { reactive } from 'vue'
import useUserStore from "./store/user"

const userData = reactive({
  username: '',
  password: '',
})

// 实例化 store
const userStore = useUserStore()

const onLogin = async () => {
  // 使用 actions,当作函数一样直接调用
  // login action 定义为了 async 函数,所以它返回一个 Promise
  await userStore.login()
  userData.username = ''
  userData.password = ''
}

const onLogout = () => {
  userStore.logout()
}
</script>

<template>
  <div>
    <!-- state:通过 store 直接访问 -->
    <template v-if="userStore.token">
       {{ userStore.username }}
       <br />
       <button @click="onLogout">退 出</button>
    </template>

    <template v-else>
      用户名:<input v-model="userData.username" />
      <br />
      密码:<input v-model="userData.password" type="password"/>
      <br />
      <button @click="onLogin">登 录</button>
    </template>
  </div>
</template>

运行代码,效果如图:

login-demo

pinia 状态持久化

Pinia 的数据是存在内存当中的,页面刷新数据就会丢失。所以对于一些重要数据,需要持久化到本地存储,简单的数据可以直接调用 localStorage 或者 sessionStorage API。更推荐的方式,是使用持久化插件,比如 pinia-plugin-persistedstate

安装:

pnpm add pinia-plugin-persistedstate

然后,在创建 pinia 实例的时候,进行插件的注册:

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

export default pinia

Pinia 中的状态是以 store 为单位进行管理的。哪个 store 中的数据需要持久化,就在哪个 store 中去开启。比如:

// store/user.js

const useUserStore = defineStore('user', {
  persist: true,
  
  // ......
})

通过安装、注册插件、给 store 增加 persist 属性,就完成了持久化。

默认,持久化的数据放在 localStorage 中,key 就是该 storeid,存储的结构就是 state 的类型:

image-20221010231550229

可以通过 persist 进行具体的设置,比如:

const useUserStore = defineStore('user', {
  persist: {
    key: "USER",
    storage: sessionStorage,
    paths: ["token"]
  },
  
  // ......
})

这样设置的效果是,数据存储在 sessionStorage 中,key 是 USER,只持久化 token 这个状态:

image-20221010232522997

结语

我们用了一个常见的登录场景,先注册好 pinia 插件,然后定义需要管理的数据(状态)和方法(登录逻辑),然后在组件中初始化 store,并使用数据,调用方法,演示了使用 Pinia 的基本流程,最后还介绍了一个持久化插件,帮助持久化 Pinia 中的状态。

从这个过程中很明显体会到,Pinia 的使用相比 Vuex ,API 更加简单,数据的流转也更加清晰。如果你还没有使用 Pinia,强烈推荐!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一个登录案例学会 Pinia 的相关文章

  • 如何使用 HTML 5 实现类似 gmail 的文件上传/附件

    我记得一些支持 Ajax 之类的选项 无回发世界 文件上传 隐藏的 iframe 使用 flash 对象 尽管我仍然好奇为什么使用 SWF 以及它提供什么优势 然而 通过查看博客 HTML 5 似乎很有前途 我尝试了一些小示例 它确实有效
  • MailTo 从 Javascript

    我有一个链接按钮 用于从页面内容构建邮件 从 javascript 启动它而不打开空白窗口或干扰调用它的窗口的最佳方法是什么 function Email var sMailTo mailto var sBody var alSelecte
  • 如果没有“new”,则无法调用类构造函数

    感谢这个问题已经被问过几次了 但是我遇到的几乎所有情况都是有人试图扩展非本地类的情况 我的情况有所不同 我有一个非常简单的基类 名为CObject如下 export class CObject extends BaseObject cons
  • 使用 Firefox 插件 sdk 的 nsISocketTransportService

    我正在尝试使用 Firefox 来读取 SSH 横幅 IE 当您最初连接到 SSH 服务器时 服务器会向您发送其横幅 标识服务器软件 并且您向 SSH 服务器发送您的横幅 标识您的客户端软件 为此 我使用以下 URL 中的示例 firefo
  • 在随机位置启动 HTML5

    我有一个大约 2 小时长的音轨 我想在我的网站上使用它 我希望它在页面加载时在随机位置开始播放曲目 使用 HTML5 可以吗 我知道您可以使用 element currentTime 函数来获取当前位置 但是如何在完全下载之前获取曲目的总时
  • Mapbox GL 中的 MaxBounds 和自定义非对称填充

    我有一个 Mapbox GL JS 应用程序 在地图上显示一些小部件 为了确保地图上的任何内容都不会被它们隐藏 我使用以下命令添加了一些填充map setPadding 这是一个不对称的 在我的例子中左边比右边大 它按预期工作 例如fitB
  • API 使用令牌向 odoo 进行身份验证

    我想使用令牌从 Express 应用程序向 Odoo 进行身份验证 我在用odoo xmlrpc https www npmjs com package odoo xmlrpc连接 Odoo 的节点模块 我的快递应用程序 Odoo 要求 A
  • React JS 服务器端问题 - 找不到窗口

    你好 我正在尝试在我的reactJS项目中使用react rte 我有服务器端渲染 每次我想使用这个包时 我都会得到 return msie 6 9 b test window navigator userAgent toLowerCase
  • 如何在 React Native 上显示 SVG 文件?

    我想显示 svg 文件 我有一堆 svg 图像 但我找不到显示的方式 我尝试使用Image and Use的组成部分反应本机 svg https github com magicismight react native svg但他们不这样做
  • 检索 css3 缩放元素的宽度/高度

    我正在与 offsetWidth 属性的奇怪之处 我认为 作斗争 这是场景 比方说 我有一个span标签 在我的js中 在某个时刻我执行css3转换 对于这个元素 例如 el set styles transform scale scale
  • 如果没有 /// 标签,TypeScript 的“将 JavaScript 输出合并到文件中”选项无法推断出正确的脚本顺序

    我正在开发一个使用 将 JavaScript 输出合并到文件中 选项的 TypeScript 解决方案 我经常引用另一个文件中定义的项目 例如 In MyBaseClass ts export class MyBaseClass In My
  • 如何将 arraylist 从 servlet 传递到 javascript?

    我通过在属性中设置数组列表并将其转发到 jsp 来从 servlet 传递数组列表 Servlet ArrayList
  • 当我多次调用 requestAnimationFrame 时会发生什么

    我的意思是一次调用多个具有相同功能的 requestAnimationFrame function Draw DoSomething function AFunc prepare something requestAnimationFram
  • ES6继承:使用`super`访问父类的属性

    JavaScript 的super关键字 当我在 Chrome Babel TypeScript 上运行代码时 得到了不同的结果 我的问题是哪个结果是正确的 规范的哪一部分定义了这种行为 下面的代码 class Point getX con
  • 如何使用 window.onerror 捕获所有 javascript 错误? (包括道场)

    这个问题是后续问题javascript 如何在弹出警报中显示脚本错误 https stackoverflow com questions 2604976 javascript how to display script errors in
  • 模板中带有 ng-if 的 angularjs 指令

    我正在构建一个在模板内使用 ng if 的指令 奇怪的是 提供给链接函数的元素没有扩展ng if代码 它只是ng if的注释行 经过一番尝试 我发现通过将链接代码包装在 timeout 中似乎可以使其正常工作 但我想知道这是否不是正确的处理
  • 如何在画布上所有其他内容后面绘制图像? [复制]

    这个问题在这里已经有答案了 我有一块画布 我想用drawImage在画布上当前内容后面绘制图像 由于画布上已经有内容 我正在使用字面上的画布来创建包含图像的画布 因此我无法真正先绘制图像 所以我无法使用drawImage在我呈现其余内容之前
  • Service Worker 与 Shared Worker

    Service Worker 和 Shared Worker 有什么区别 我什么时候应该使用 Service Worker 而不是 Shared Worker 反之亦然 Service Worker 具有共享 Worker 之外的附加功能
  • 尝试使用 Javascript 解决对称差异

    我正在尝试找出对称的解决方案 使用 javascript 完成以下任务的差异 目标 接受未指定数量的数组作为参数 保留数组中数字的原始顺序 不删除单个数组中数字的重复项 删除数组中出现的重复项 因此 例如 如果输入是 1 1 2 6 2 3
  • PDF 在 Safari 中隐藏 Jquery Modal

    这是与我有关的事情this https stackoverflow com questions 24052681 pdf hide jquery modal in ie问题 在 IE 中 我在对话框中使用 iframe 解决了问题 所以它工

随机推荐

  • python中circle函数的用法,python画圆运用了什么函数

    python画圆运用了matplotlb库的figure 和Circle 函数 其中 figure 函数用于确定画布大小 而Circle 函数用于配置圆的相关信息 进而画圆 本教程操作环境 windows7系统 Python3版 Dell
  • MySQL多表操作练习题

    数据准备 CREATE table dept deptno INT PRIMARY KEY dname VARCHAR 14 loc VARCHAR 13 INSERT INTO dept VALUES 10 accounting new
  • QT开发(十九)——QT内存泄漏问题

    QT开发 十九 QT内存泄漏问题 一 QT对象间的父子关系 QT最基础和核心的类是 QObject QObject内部有一个list 会保存children 还有一个指针保存parent 当自己析构时 会自己从parent列表中删除并且析构
  • Java 反射 与 主要API

    控制你的大脑 控制你的行为 你会得到更多 收获很多 文章目录 一 反射相关的主要API 二 代码例子演示 三 反射测试类 一 反射相关的主要API API 名称 代表含义 Java lang class 代表一个类 java lang re
  • J-Tech Talk | 6.29首播 Python文档漫谈

    J Tech Talk 由 Jina AI 社区为大家带来的技术分享 围绕 Python 人工智能 深度学习等 给大家带来针对具体实战型问题的讲解 分享 Jina AI 在开发过程中所积累的经验 Github 的开源项目数不胜数 想要扩大项
  • 使用OPENLDAP C API修改 win2003 AD域(Active Directory)用户密码

    参考 http blog csdn net wzhwho article details 6209693 参考 http www 121 name LDAP html 参考 http pig made it com pig adusers
  • 单片机学习笔记1:单片机简介

    单片机 1 什么是单片机 单片机 Single Chip Microcomputer 是一种集成电路芯片 是采用超大规模集成电路技术把具有数据处理能力的中央处理器 CPU 存储器 RAM ROM 中断系统 I O接口电路 定时器 计数器等功
  • 图像分类(1),数据预处理

    本文介绍如何使用pytorh利用预训练模型进行图像分类 主要参考Transfer Learning Tutorial和 具体代码可以参考Image classification 下载代码文件 git clone https github c
  • Web自动化测试,怎样参数化?

    目录 1 读取 csv 文件 2 数据ddt参数化 2 1实施步骤 2 2代码举例 unittest框架 3 PO 模式 3 1介绍 3 2非po模式和po模式对比 3 3Po分层及彼此关系 3 4代码举例 非PO模式 3 5代码举例 PO
  • 计算机开机后发现不了硬盘 首先应该检查,电脑每次开机都检测硬盘,怎么关闭这项功能?...

    如何关闭硬盘开机检测 开机自检时可以通过以下方法关闭硬盘的智能状态 不检测是无法实现的 hard disk state at all 1 Boot into bios 通常按f1 f2 f9 del 键进入bios 2 在boot或adva
  • torch一些常见的环境问题

    前言 我们在使用torch的时候经常会遇到一些常见的错误 这里单独开一篇博客记录一下 以便日后翻阅 本文会不断更新 torch1 13 有时候pip 下某个包的时候经常会自动安装nvidia cublas cu11 11 10 3 66 于
  • const int *p和int * const p的区别(常量指针与指向常量的指针)

    对于指针和常量 有以下三种形式都是正确的 const char myPtr char A 指向常量的指针 char const myPtr char A 常量的指针 const char const myPtr char A 指向常量的常量
  • Kali使用Netdiscover探测局域网中存活主机

    1 netdiscover介绍 Netdiscover 是一个主动 被动的ARP 侦查工具 使用Netdiscover工具可以在网络上扫描IP地址 检查在线主机或搜索为它们发送的ARP请求 2 主动模式 主动模式顾名思义就是主动的探测发现网
  • 使用键值表实现通用流水号

    很多MIS系统 都需要用到流水号 一般的简单的流水号 由标识 日期 自增序号来组成 但如果考虑通用的话 就稍微复杂点儿的 需要考虑自定义日期格式 自增序号归1 自增序号溢出处理 前缀 中缀 后缀 并发访问 批量获取等 本文抽象出一个通用的生
  • Spring学习(三)IOC控制反转与DI依赖注入

    IOC Inversion of Control 控制反转 是spring的核心 贯穿始终 所谓IOC 对于spring框架来说 就是由spring来负责控制对象的生命周期和对象间的关系 传统开发模式 对象之间互相依赖 IOC开发模式 IO
  • eclipse 项目提示"Project facet Java version 1.8 is not supported"

    今天使用eclipse 工具 导入maven 项目 编译项目的时候提示 Project facet Java version 1 8 is not supported 第一步 排查eclipse 项目的编译版本 项目 properties
  • Angular-官方文档学习-1

    Angular 简介 AngularJS 是一个 JavaScript 框架 它可通过 AngularJS 通过 指令 扩展了 HTML 且通过 表达式 绑定数据到 HTML AngularJS 扩展了 HTML AngularJS 通过
  • 选择文件窗口,获取选择文件地址

    微信公众号原文 系统 Windows 7 软件 Excel 2010 学习路径图 针对之前的学习路径图 会针对的写一些文章 我们在做信息处理的时候 可能会涉及到多个其它文件 有的时候需要根据需求选择所需文件进行处理 今天我们就讲讲如何使用V
  • 软件测试的基本概念

    目录 一 什么是需求 二 什么是测试用例 三 什么是BUG 四 开发模型和测试模型 1 软件开发生命周期 2 软件开发的五大模型 2 1 瀑布模型 2 2螺旋模型 2 3增量模型 迭代模型 2 4 敏捷模型 3 软件测试的两大模型 3 1
  • 一个登录案例学会 Pinia

    Pinia 号称下一代的 Vuex 经过初步体验 发现相比于 Vuex Pinia 确实有了很大进步 最明显的就是删减了复杂的概念 简化了数据流转的过程 现在只剩下了 store state getters actions 这四个核心概念