Pinia从入门到精通

2023-10-26

一、为什么使用 Pinia

Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。如果你熟悉组合式 API 的话,你可能会认为可以通过一行简单的 export const state = reactive({}) 来共享一个全局状态。对于单页应用来说确实可以,但如果应用在服务器端渲染,这可能会使你的应用暴露出一些安全漏洞。 而如果使用 Pinia,即使在小型单页应用中,你也可以获得如下功能:

  • Devtools 支持
    • 追踪 actions、mutations 的时间线
    • 在组件中展示它们所用到的 Store
    • 让调试更容易的 Time travel
  • 热更新
    • 不必重载页面即可修改 Store
    • 开发时可保持当前的 State
  • 插件:可通过插件扩展 Pinia 功能
  • 为 JS 开发者提供适当的 TypeScript 支持以及自动补全功能。
  • 支持服务端渲染

二、 对比 Vuex

与 Vuex 相比,Pinia 不仅提供了一个更简单的 API,也提供了符合组合式 API 风格的 API,最重要的是,搭配 TypeScript 一起使用时有非常可靠的类型推断支持。

对比 Vuex 3.x/4.x

Vuex 3.x 只适配 Vue 2,而 Vuex 4.x 是适配 Vue 3 的。

Pinia API 与 Vuex(<=4) 也有很多不同,即:

  • mutation 已被弃用。它们经常被认为是极其冗余的。它们初衷是带来 devtools 的集成方案,但这已不再是一个问题了。
  • 无需要创建自定义的复杂包装器来支持 TypeScript,一切都可标注类型,API 的设计方式是尽可能地利用 TS 类型推理。
  • 无过多的魔法字符串注入,只需要导入函数并调用它们,然后享受自动补全的乐趣就好。
  • 无需要动态添加 Store,它们默认都是动态的,甚至你可能都不会注意到这点。注意,你仍然可以在任何时候手动使用一个 Store 来注册它,但因为它是自动的,所以你不需要担心它。
  • 不再有嵌套结构的模块。你仍然可以通过导入和使用另一个 Store 来隐含地嵌套 stores 空间,虽然是 Pinia 从设计上提供的是一个扁平的结构,但仍然能够在 Store 之间进行交叉组合。你甚至可以让 Stores 有循环依赖关系
  • 不再有可命名的模块。考虑到 Store 的扁平架构,Store 的命名取决于它们的定义方式,你甚至可以说所有 Store 都应该命名。

关于如何将现有 Vuex(<=4) 的项目转化为使用 Pinia 的更多详细说明,请参阅 Vuex 迁移指南

三、安装pinia

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

 两种创建实例的方式

1.直接在 main.js 创建

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

const pinia = createPinia()    // 创建实例
const app = createApp(App)

app.use(pinia)    // 安装插件
app.mount('#app')

2.以上创建方式方便很多但是后期如果我们需要添加扩展内容的话 main.js 会变得很大,所以比较推荐以下这种创建方式,在以下的案例中,都会以以下的方式来说明

src/store/index.js

import { createPinia } from 'pinia'   //创建pinia
const pinia = createPinia()   //实例化

export default pinia  

 main.js

import { createApp } from 'vue'
import App from './App.vue'
import pinia from './store' //引入创建好的pinia实例

const app = createApp(App)

app.use(pinia)  //全局挂载
app.mount('#app')

四、Store 是什么?

Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念stategetter 和 action,我们可以假设这些概念相当于组件中的 data、 computed 和 methods

定义 Store  (src/store/counter.js)

defineStore() 的第二个参数可接受两类值:Setup 函数或 Option 对象。

Option Store:与 Vue 的选项式 API 类似,我们也可以传入一个带有 stateactions 与 getters 属性的 Option 对象

import { defineStore } from 'pinia'

// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,
// 同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useStore = defineStore('main', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

// 你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),
// 而 actions 则是方法 (methods)。

Setup Store:也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。

import { defineStore } from 'pinia'
import { computed, ref } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)

  const double = computed(() => count.value * 2)

  function increment () {
    count.value++
  }

  return { count, double, increment }
})

// 在 Setup Store 中:

// ref() 就是 state 属性
// computed() 就是 getters
// function() 就是 actions

使用 Store   (src/App.vue)

<template>
  <div>
    <h1>我是app</h1>

    <!-- 具有响应式 -->
    {{ counter.count }}
  </div>
</template>

<script setup>
import { useCounterStore } from './store/counter'
import { storeToRefs } from 'pinia'
const counter = useCounterStore()

// ❌ 这将无法生效,因为它破坏了响应性
// 这与从 `props` 中解构是一样的。
const { count, double } = counter


// `name` and `doubleCount` 都是响应式 refs
// 这也将为由插件添加的属性创建 refs
// 同时会跳过任何 action 或非响应式(非 ref/响应式)属性
const { count, double } = storeToRefs(counter)
// 名为 increment 的 action 可以直接提取
const { increment } = counter

</script>

五、State

访问 state

import { useCounterStore } from './store/counter' // 导入创建好的counter.js
const counter = useCounterStore()    // 实例化

console.log(counter.count)    // 0

重置 state

import { useCounterStore } from './store/counter' // 导入创建好的counter.js
const counter = useCounterStore()    // 实例化

counter.$reset()    // store 的 $reset() 方法将 state 重置为初始值。

变更 state

<script setup>
import { useCounterStore } from './store/counter'
const counter = useCounterStore()

// 方式1 
counter.count++

//以下两种方式可以一次性修改多个值

// 方式2: $patch 对象写法
counter.$patch({
  count: counter.count + 1,
  .....
})

// 方式3: $patch 函数写法
counter.$patch((state) => {
  // state 是 counter里的state
  state.count = state.count + 1
  .....
})

// 方式4: 通过 actions 创建的函数修改
counter.increment()

</script>

替换 state

<script setup>
import { useCounterStore } from './store/counter'
import pinia from './store/index';  // 挂载的pinia实例
const counter = useCounterStore()

// 你不能完全替换掉 store 的 state,因为那样会破坏其响应性。但是,你可以 patch 它。

// 这实际上并没有替换`$state`
counter.$state = { count: 24 }
// 只是在它内部调用 `$patch()`:
counter.$patch({ count: 24 })


// 你也可以通过变更 pinia 实例的 state 来设置整个应用的初始 state。这常用于 SSR 中的激活过程。
pinia.state.value = {}

</script>

订阅 state

<script setup>
import { useCounterStore } from './store/counter'
const counter = useCounterStore()

// 必须先订阅在修改才会触发
counter.$subscribe((mutation, state) => {
  console.log(mutation)
  console.log(state)
  /**
   * 其中 state 是 mainStore 实例,
   * 而 mutation mutation对象主要包含三个属性
        events : 是这次state改变的具体数据,包括改变前的值和改变后的值等等数据
        storeId :是当前store的id
        type:type表示这次变化是通过什么产生的,主要有三个分别是
          “direct” :通过 action 变化的
          ”patch object“ :通过 $patch 传递对象的方式改变的
          “patch function” :通过 $patch 传递函数的方式改变的
   */

  // 每当状态发生变化时,将整个 state 持久化到本地存储。
  localStorage.setItem('counter', JSON.stringify(state))
})

// 修改state值
counter.increment()

</script>

默认情况下,state subscription 会被绑定到添加它们的组件上(如果 store 在组件的 setup() 里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将 { detached: true } 作为第二个参数,以将 state subscription 从当前组件中分离

<script setup>
import { useCounterStore } from './store/counter'
const counter = useCounterStore()

// 必须先订阅在修改才会触发
counter.$subscribe(callback, { detached: true })

// 修改state值
counter.increment()

</script>

六、Getters

export const useCounterStore = defineStore('Counter',{
  state: () => {
    return {
      name: '快乐超人',
    }
  },
  getters: {
    formatName: (state) => {
      return state.name + '00';
    },
  },
})
import { useCounterStore } from './store/counter'
const counter = useCounterStore()

counter.formatName    //快乐超人00

如果需要向 getters 定义的函数传入参数:

export const useCounterStore = defineStore('Counter', {
  getters: {
    getUserById: (state) => {
      return (userId) => state.users.find((user) => user.id === userId)
    },
  },
})
import { useCounterStore } from './store/counter'
const counter = useCounterStore()

counter.getUserById(2)

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

Pinia从入门到精通 的相关文章

随机推荐

  • 乐蜂网服务器信息,乐蜂网目标独立上市 唯品会向其派驻CEO、CFO

    腾讯科技讯 王可心 2月25日消息 在乐蜂网 今日 唯品会副总裁冯佳路 乐蜂网副总裁辛益华接受媒体采访 解答外界疑问 控股乐蜂网后 为何又参股东方风行集团 在过去10天 唯品会与乐蜂网 东方风行集团分别发生交易 2月14日 唯品会宣布战略投
  • 爬虫的工作原理、挑战和应用

    什么是网络爬虫 网络爬虫 Web Crawler 是一种自动化程序 它能够在互联网上浏览网页 收集信息并将其存储在本地或其他地方供进一步处理和分析 爬虫通常用于搜索引擎 数据挖掘 内容聚合 价格比较等应用中 爬虫的工作原理 发送请求 爬虫从
  • Node.js 全网最详细教程(第三章:Node.js 文件系统模块)

    fs 文件系统模块 fs 模块是Node js 官方提供的 用来操作文件的模块 它提供了一系列的方法和属性 用来满足用户对文件的操作需求 一 fs readFile 异步读取文件 源码解析 参数1 path 读取的路径 参数2 option
  • ProGuard 进阶系列(二)配置解析

    书接上文 从开源库中把代码下载到本地后 就可以在 IDE 中进行运行了 从 main 方法入手 可以看到 ProGuard 执行的第一步就是去解析参数 本文的内容主要分析源码中我们配置的规则解析的实现 在上一篇文章末尾 在 IDE 中 添加
  • WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connec

    WARNING Retrying Retry total 0 connect None read None redirect None status None after connection broken by SSLError SSLE
  • C语言函数不定参数实现方式

    函数如何实现不定参数 由于在C语言中没有函数重载 解决不定数目函数参数问题变得比较麻烦 即使采用C 如果参数个数不能确定 也很难采用函数重载 对这种情况 提出了指针参数来解决问题 1 va list 定义了一个指针arg ptr 用于指示可
  • 数据库设计规范(详细)

    数据规范化设计 一 数据规范化 仅有好的RDBMS并不足以避免数据冗余 必须在数据库的设计中创建好的表结构 Dr E F codd 最初定义了规范化的三个级别 范式是具有最小冗余的表结构 这些范式是 1 第一范式 1st NF First
  • linux配置浮动IP

    在高可用集群环境中 一般都需要使用浮动IP来实现web高可用 High Availability 浮动IP的概念以及为什么需要浮动IP请参考 浮动IP FLOAT IP 本篇文章主要讲实际操作步骤 可以是双机 也可以是多机 主服务器为172
  • VLOOKUP函数匹配结果不正确?错误结果是怎么得出来的?[已解决]

    匹配结果不正确主要是以下2个方面入手 是否是精确匹配 range lookup 参数为 FALSE或0 检查所有的数据是否带空格等 官方帮助文档 Microsoft Excel 中 VLOOKUP 函数的语法和用法 语法 VLOOKUP l
  • [yotroy.cool]WSL安装CUDA独显 PyTorch 深度学习环境配置,测速为什么要用WSL?

    个人博客https www yotroy cool 欢迎关注我哦 前言 为了预习大三课程 想提前学习下PyTorch 于是我遇到了神仙学习教程 动手学深度学习 同时以此为参考完成了环境配置 感谢大佬们无私奉献Thanks 本教程展示了独显w
  • 等精度测频的原理和基于FPGA的实现

    我们通过FPGA测量信号频率 一般来说有两种方案 传统测频法和等精度测频法 方案一 传统测频是在一段闸门时间内直接对输入信号的周期进行计数 也被叫做直接测频法 设闸门信号为gate 检测待测信号上升沿 然后判断gate是否为高电平 若为高电
  • 转置矩阵,逆矩阵和倒转置矩阵

    单位矩阵 转置矩阵 transpose matrix 在线性代数中 矩阵A的转置是另一个矩阵AT 也写做Atr tA或A 由下列等价动作建立 把A的横行写为AT的纵列 把A的纵列写为AT的横行 形式上说 m n矩阵A的转置是n m矩阵 fo
  • 【ctfshow】PHP特性2

    目录 web100 web101 web102 web103 web104 web105 web106 web107 web108 web109 web110 web100 include ctfshow php flag in class
  • ascii码图片

    铁臂阿童木 be be Nc R o uQ bo Jod e dd e d
  • lora模块学习一

    LoRa TM 调制解调器采用扩频调制和前向纠错技术 与传统的FSK或OOK调制技术相比 这种技术不仅扩大了无线通讯链路的覆盖范围 而且还提高了链路的鲁棒性 在设计中 可以通过调整扩频因子 SF 调制带宽 BW 和编码率 CR 三个关键设计
  • cuda-gdb 调试方法:cuda gdb中的可调参数

    cuda gdb 中存在一些可调的参数 可以通过 set cuda lt tunable name gt
  • php实现ETH原生签名交易sendRowTransaction

    文章目录 1 安装GMP 之前需要先安装m4 不然会出错 2 然后ubuntu安装gmp 3 然后安装PHP gmp扩展 4 安装scrypt 扩展 4 撤销sudoers文件写权限 命令 参考文章链接 1 安装GMP 之前需要先安装m4
  • window.location.href跳转带有token的新链接时显示空白页

    问题 从A平台跳到B平台并自动登录B平台 后端生成包含token的完整链接传给前端 前端window location href打开时页面显示空白页 手动刷新才会跳到新链接 原因 用window location href跳转新链接可能会带
  • JIO学习(一)输入流综述

    一 字节输入流 java io InputStream java lang Object java io InputStream 所有已实现的接口 Closeable 直接已知子类 AudioInputStream ByteArrayInp
  • Pinia从入门到精通

    一 为什么使用 Pinia Pinia 是 Vue 的专属状态管理库 它允许你跨组件或页面共享状态 如果你熟悉组合式 API 的话 你可能会认为可以通过一行简单的 export const state reactive 来共享一个全局状态