Pinia学习

Vue3中 使用

官网:https://pinia.web3doc.top/introduction.html

安装

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

使用

main.js中引入并注册

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import './style.css'
import App from './App.vue' const app = createApp(App)
app.use(createPinia())
app.mount('#app')

src下创建store文件夹-用于管理state

// 命名规范
// use + 逻辑名 || 业务名 + store
// useCounterStore

Store

创建

import { defineStore } from 'pinia'
// defineStore
// 参数一是一个id 参数二是配置项 state getters actions
export const useCounterStore = defineStore('counter', {
state: () => {
return {
count: 0
}
},
getters: {
doubleCount: () => state.count * 2
},
actions: {
increment () {
this.count++
},
decrement () {
this.count--
}
}
})

Store的 使用

<script setup>
import { useCounterStore } from './store/useCounterStore'
import { storeToRefs } from 'pinia' // store 是一个用reactive 包裹的对象
// 这意味着不需要在getter 之后写.value,但是,就像setup 中的props 一样,我们不能对其进行解构:
const store = useCounterStore()
// 想结构后保持其响应式 就必须借用 storeToRefs()
const { count } = storeToRefs(store)
console.log('我创建的第一个仓库', store);
console.log('仓库结构', count); // ref包裹的对象
</script>

Store知识点

  1. defineStore 创建仓库

参数1 id 参数2 配置项

返回值 reactive包裹的响应式对象

  1. 引入 ==》 调用 ==》 结构

    import { useCounterStore } from './store/useCounterStore'

    import { storeToRefs } from 'pinia'

    const store = useCounterStore()

    const { count } = storeToRefs(store)

  2. 解构时 直接结构调用后的返回值(store)得到的是不具备响应式的数据

    需要通过storeToRefs () 包裹 store 解构出来的数据才具备响应式 (ref)

State

创建

export const useCounterStore = defineStore("counter", {
state: () => {
return {
num: 0,
count:0
};
}
})

访问状态

默认情况下,您可以通过 store 实例访问状态来直接读取和写入状态:

<script setup>
import { useCounterStore } from './store/useCounterStore'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const { count } = storeToRefs(store)
console.log('我创建的第一个仓库', store);
console.log('仓库结构', count); // ref包裹的对象

重置状态

原理

调用的$patch

const $reset = isOptionsStore

​ ? function $reset(this: _StoreWithState<Id, S, G, A>) {

​ const { state } = options as DefineStoreOptions<Id, S, G, A>

​ const newState = state ? state() : {}

​ // we use a patch to group all changes into one single subscription

​ this.$patch(($state) => {

​ assign($state, newState)

​ })

​ }

​ : /* istanbul ignore next */

DEV

​ ? () => {

​ throw new Error(

: Store "${$id}" is built using the setup syntax and does not implement $reset().

​ )

​ }

​ : noop

你可以使用store中的 $reset 进行状态重置

  const store = useCounterStore()
store.$reset()

使用选项API

// 仓库文件
import { defineStore } from "pinia"; // 第二个参数支持两种风格:options api 以及 composition api
export const useCounterStore = defineStore("counter", {
state: () => {
return {
num: 0,
};
}
})

组合API

import { defineStore } from "pinia";
import { reactive } from "vue";
export const useListStore = defineStore("list", () => {
const counterStore = useCounterStore();
// 组合 api 风格 // 创建仓库数据,类似于 state
const list = reactive({
items: [
{
text: "学习 Pinia",
isCompleted: true,
},
{
text: "打羽毛球",
isCompleted: false,
},
{
text: "玩游戏",
isCompleted: true,
},
],
counter: 100,
});
return {
list
}
})

非组合API

如果您不使用 Composition API,并且使用的是 computedmethods、...,则可以使用 mapState() 帮助器将状态属性映射为只读计算属性:

import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore' export default {
computed: {
// 允许访问组件内部的 this.counter
// 与从 store.counter 读取相同
...mapState(useCounterStore, {
myOwnName: 'counter',
// 您还可以编写一个访问 store 的函数
double: store => store.counter * 2,
// 它可以正常读取“this”,但无法正常写入...
magicValue(store) {
return store.someGetter + this.counter + this.double
},
}),
},
}

可修改状态 mapWritableState

如果您希望能够写入这些状态属性(例如,如果您有一个表单),您可以使用 mapWritableState() 代替。 请注意,您不能传递类似于 mapState() 的函数:

import { mapWritableState } from 'pinia'
import { useCounterStore } from '../stores/counterStore' export default {
computed: {
// 允许访问组件内的 this.counter 并允许设置它
// this.counter++
// 与从 store.counter 读取相同
...mapWritableState(useCounterStore, ['counter'])
// 与上面相同,但将其注册为 this.myOwnName
...mapWritableState(useCounterStore, {
myOwnName: 'counter',
}),
},
}

对于像数组这样的集合,您不需要 mapWritableState(),除非您用 cartItems = [] 替换整个数组,mapState() 仍然允许您调用集合上的方法。

改变状态

方法一: 直接通过仓库实例更改

方法二: 通过$patch

// 1.
store.counter ++
// 2
store.$patch({
counter: store.counter + 1,
name: 'Abalam',
})

但是,使用这种语法应用某些突变非常困难或代价高昂:任何集合修改(例如,从数组中推送、删除、拼接元素)都需要您创建一个新集合。 正因为如此,$patch 方法也接受一个函数来批量修改集合内部分对象的情况:

cartStore.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})

替换state

您可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态:

store.$state = { counter: 666, name: 'Paimon' }

您还可以通过更改 pinia 实例的 state 来替换应用程序的整个状态。 这在 SSR for hydration 期间使用。

pinia.state.value = {}

订阅状态

可以通过 store 的 $subscribe() 方法查看状态及其变化,类似于 Vuex 的 subscribe 方法。 与常规的 watch() 相比,使用 $subscribe() 的优点是 subscriptions 只会在 patches 之后触发一次(例如,当使用上面的函数版本时)。

cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 与 cartStore.$id 相同
mutation.storeId // 'cart'
// 仅适用于 mutation.type === 'patch object'
mutation.payload // 补丁对象传递给 to cartStore.$patch() // 每当它发生变化时,将整个状态持久化到本地存储
localStorage.setItem('cart', JSON.stringify(state))
})

默认情况下,state subscriptions 绑定到添加它们的组件(如果 store 位于组件的 setup() 中)。 意思是,当组件被卸载时,它们将被自动删除。 如果要在卸载组件后保留它们,请将 { detached: true } 作为第二个参数传递给 detach 当前组件的 state subscription

export default {
setup() {
const someStore = useSomeStore() // 此订阅将在组件卸载后保留
someStore.$subscribe(callback, { detached: true }) // ...
},
}

您可以在 pinia 实例上查看整个状态:

watch(
pinia.state,
(state) => {
// 每当它发生变化时,将整个状态持久化到本地存储
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)

状态持久化

数据持久化的实现原理:

利用localStorage存储到本地

import { defineStore } from 'pinia'

const useStateStore = defineStore('state', {

  state: () => {
return {
a: 1,
b: 2,
c: false
}
},
persist: true, // 持久化配置
})
export default useStateStore
// 安装 插件
// pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

Getters

本质:computed计算属性

  1. getters语法和state一样

  2. getters 中可以接收state作为参数

  3. getters中如果要使用其他getters 不能继续使用箭头函数

  4. 使用其他store中的数据 直接在当前store引入即可

定义

export const useStore = defineStore('store', {
state:() =>{
a:1
}
getters:{
double:(state) =>{
return state.a +1
}
}
})

this

大多数时候,getter 只会依赖状态,但是,他们可能需要使用其他 getter。 正因为如此,我们可以在定义常规函数时通过 this 访问到 整个 store 的实例

但是需要定义返回类型(在 TypeScript 中)。 这是由于 TypeScript 中的一个已知限制,并且不会影响使用箭头函数定义的 getter,也不会影响不使用 this 的 getter

export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// 自动将返回类型推断为数字
doubleCount(state) {
return state.counter * 2
},
// 返回类型必须明确设置
doublePlusOne(): number {
return this.counter * 2 + 1
},
},
})

使用

直接在store实例上进行调用即可

<template>
<p>Double count is {{ store.doubleCount }}</p>
</template>
// 写法一
<script>
export default {
setup() {
const store = useStore() return { store }
},
}
</script>
// 写法二
<script setup>
export default {
const store = useStore()
}
</script>

访问其他getter

当前getter要访问其他getter,可以借助普通函数中的this

export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// 类型是自动推断的,因为我们没有使用 `this`
doubleCount: (state) => state.counter * 2,
// 这里需要我们自己添加类型(在 JS 中使用 JSDoc)。 我们还可以
// 使用它来记录 getter
/**
* 返回计数器值乘以二加一。
*
* @returns {number}
*/
doubleCountPlusOne() {
// 自动完成
return this.doubleCount + 1
},
},
})

参数传递

由于getter本质上也是计算属性,因此它本身不接收任何参数,但是我们可以通过返回函数的形式让其接收参数

export const useStore = defineStore('main', {
getters: {
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
},
})
实际使用

缺点:getter不再缓存

<script setup>
import {storeToRefs} from 'pinia'
export default {
const store = useStore()
const {getUserById} = storeToRefs(store)
}
</script> <template>
<p>User 2: {{ getUserById(2) }}</p>
</template>

Actions

Actions 相当于组件中的 methods。 它们可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑

定义

getters 一样,操作可以通过 this 访问 whole store instance 并提供完整类型(和自动完成)支持与它们不同,actions 可以是异步的

export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})

使用 setup

<script setup>
const useStore = useStore()
// 像methods一样被调用即可
useStore.increment()
</script>

不适用setup

import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counterStore' export default {
methods: {
// gives access to this.increment() inside the component
// same as calling from store.increment()
...mapActions(useCounterStore, ['increment'])
// same as above but registers it as this.myOwnName()
...mapActions(useCounterStore, { myOwnName: 'doubleCounter' }),
},
}

访问其他store

直接在内部引入使用即可

mport { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore('settings', {
state: () => ({
// ...
}),
actions: {
async fetchUserPreferences(preferences) {
const auth = useAuthStore()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
},
})

订阅

可以使用 store.$onAction() 订阅 action 及其结果。 传递给它的回调在 action 之前执行。 after 处理 Promise 并允许您在 action 完成后执行函数。 以类似的方式,onError 允许您在处理中抛出错误。 这些对于在运行时跟踪错误很有用,类似于 Vue 文档中的这个提示

const unsubscribe = someStore.$onAction(
({
name, // action 的名字
store, // store 实例
args, // 调用这个 action 的参数
after, // 在这个 action 执行完毕之后,执行这个函数
onError, // 在这个 action 抛出异常的时候,执行这个函数
}) => {
// 记录开始的时间变量
const startTime = Date.now()
// 这将在 `store` 上的操作执行之前触发
console.log(`Start "${name}" with params [${args.join(', ')}].`) // 如果 action 成功并且完全运行后,after 将触发。
// 它将等待任何返回的 promise
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
}) // 如果 action 抛出或返回 Promise.reject ,onError 将触发
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
) // 手动移除订阅
unsubscribe()

插件Plugins

插件仅适用于**在将pinia传递给应用程序后创建的 store **,否则将不会被应用。

作用

  • 向 Store 添加新属性
  • 定义 Store 时添加新选项
  • 为 Store 添加新方法
  • 包装现有方法
  • 更改甚至取消操作
  • 实现本地存储等副作用
  • 适用于特定 Store

基本用法

import { createPinia } from 'pinia'

// 为安装此插件后创建的每个store添加一个名为 `secret` 的属性
// 这可能在不同的文件中
function SecretPiniaPlugin() {
return { secret: 'the cake is a lie' }
} const pinia = createPinia()
// 将插件提供给 pinia
pinia.use(SecretPiniaPlugin) // 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'

参数

Pinia 插件是一个函数,可以选择返回要添加到 store 的属性。 它需要一个可选参数,一个 context

export function myPiniaPlugin(context) {
context.pinia // 使用 `createPinia()` 创建的 pinia
context.app // 使用 `createApp()` 创建的当前应用程序(仅限 Vue 3)
context.store // 插件正在扩充的 store
context.options // 定义存储的选项对象传递给`defineStore()`
// ...
} pinia.use(myPiniaPlugin)

扩充store

插件的任何属性 returned 都会被devtools自动跟踪,所以为了让hello在devtools中可见,如果你想调试它,请确保将它添加到store._customProperties仅在开发模式 开发工具:

// 从上面的例子
pinia.use(({ store }) => {
store.hello = 'world'
// 确保您的打包器可以处理这个问题。 webpack 和 vite 应该默认这样做
if (process.env.NODE_ENV === 'development') {
// 添加您在 store 中设置的任何 keys
store._customProperties.add('hello')
}
})

请注意,每个 store 都使用 reactive 包装,自动展开任何 Ref (ref(), computed() , ...) 它包含了:

这就是为什么您可以在没有 .value 的情况下访问所有计算属性以及它们是响应式的原因。

const sharedRef = ref('shared')
pinia.use(({ store }) => {
// 每个 store 都有自己的 `hello` 属性
store.hello = ref('secret')
// 它会自动展开
store.hello // 'secret' // 所有 store 都共享 value `shared` 属性
store.shared = sharedRef
store.shared // 'shared'
})

添加新状态

请注意,插件中发生的状态更改或添加(包括调用store.$patch())发生在存储处于活动状态之前,因此不会触发任何订阅

  • store 上,因此您可以使用 store.myState 访问它
  • store.$state 上,因此它可以在 devtools 中使用,并且在 SSR 期间被序列化
onst globalSecret = ref('secret')
pinia.use(({ store }) => {
// `secret` 在所有 store 之间共享
store.$state.secret = globalSecret
store.secret = globalSecret
// 它会自动展开
store.secret // 'secret' const hasError = ref(false)
store.$state.hasError = hasError
// 这个必须始终设置
store.hasError = toRef(store.$state, 'hasError') // 在这种情况下,最好不要返回 `hasError`,因为它
// 将显示在 devtools 的 `state` 部分
// 无论如何,如果我们返回它,devtools 将显示它两次。
})

插件中使用订阅

pinia.use(({ store }) => {
store.$subscribe(() => {
// 在存储变化的时候执行
})
store.$onAction(() => {
// 在 action 的时候执行
})
})

pinia的更多相关文章

  1. 轻量级状态管理库Pinia试吃

      最近连续看了几个GitHub上的开源项目,里面都用到了 Pinia 这个状态管理库,于是研究了一下,发现确实是个好东西!那么,Pinia 的特点: 轻量化 -- Pinia 体积约1KB,十分轻巧 ...

  2. 简单了解一下pinia的结构

    随着 Vue3 的正式转正,Pinia 也渐渐火了起来.所以要更新一下自己的知识树了.这里主要是看看新的状态是什么"形态". 状态的容器还是"reactive" ...

  3. 在Vue3项目中使用pinia代替Vuex进行数据存储

    pinia是一个vue的状态存储库,你可以使用它来存储.共享一些跨组件或者页面的数据,使用起来和vuex非常类似.pina相对Vuex来说,更好的ts支持和代码自动补全功能.本篇随笔介绍pinia的基 ...

  4. vue下一代状态管理Pinia.js 保证你看的明明白白!

    1.pinia的简单介绍 Pinia最初是在2019年11月左右重新设计使用Composition API的 Vue 商店外观的实验. 从那时起,最初的原则相同,但 Pinia 适用于 Vue 2 和 ...

  5. 结合 Vuex 和 Pinia 做一个适合自己的状态管理 nf-state

    一开始学习了一下 Vuex,感觉比较冗余,就自己做了一个轻量级的状态管理. 后来又学习了 Pinia,于是参考 Pinia 改进了一下自己的状态管理. 结合 Vuex 和 Pinia, 保留需要的功能 ...

  6. 一文解析Pinia和Vuex,带你全面理解这两个Vue状态管理模式

    Pinia和Vuex一样都是是vue的全局状态管理器.其实Pinia就是Vuex5,只不过为了尊重原作者的贡献就沿用了这个看起来很甜的名字Pinia. 本文将通过Vue3的形式对两者的不同实现方式进行 ...

  7. pinia 入门及使用

    自上月从上海结束工作回来 在家闲来无事 想写点东西打发时间 也顺便学习学习新的技术.偶然发现了 pinia 据说比vuex好用些 所以便搭了个demo尝试着用了下 感觉确实不错,于是便有了这篇随笔. ...

  8. 基于 vite 创建 vue3 全家桶项目(vite + vue3 + tsx + pinia)

    vite 最近非常火,它是 vue 作者尤大神发布前端构建工具,底层基于 Rollup,无论是启动速度还是热加载速度都非常快.vite 随 vue3 正式版一起发布,刚开始的时候与 vue 绑定在一起 ...

  9. 使用Vite快速构建Vue3+ts+pinia脚手架

    一.前言 vue3的快速更新,很多IT发展快的地区在22开始都已经提上日程,小编所在的青岛好像最近才有点风波.vue3的人才在青岛还是比较稀缺的哈,纯属小编自己的看法,可能小编是个井底之蛙!! vue ...

  10. vue3中pinia的使用总结

    pinia的简介和优势: Pinia是Vue生态里Vuex的代替者,一个全新Vue的状态管理库.在Vue3成为正式版以后,尤雨溪强势推荐的项目就是Pinia.那先来看看Pinia比Vuex好的地方,也 ...

随机推荐

  1. TiKV占用内存超过的解决过程

    TiKV占用内存超过的解决过程 背景 为了后去TiDB的极限数据. 晚上在每台服务器上面增加了多个TiKV的节点. 主要方式为: 每个NVME的硬盘增加两个TiKV的进程. 这样每个服务器两个磁盘, ...

  2. [转帖]Kafka 性能优化与问题深究

    Kafka 性能优化与问题深究 一.Kafka深入探究 1.1 kafka整体介绍 1. 1.1 Kafka 如何做到高吞吐.低延迟的呢? Kafka是一个分布式高吞吐量的消息系统,这里提下 Kafk ...

  3. [转帖]Optimizing Block Device Parameter Settings of Linux

    https://support.huawei.com/enterprise/en/doc/EDOC1000181485/ddbc0e8b/optimizing-block-device-paramet ...

  4. [转帖]linux 系统级性能分析工具 perf 的介绍与使用

    目录 1. 背景知识 1.1 tracepoints 1.2 硬件特性之cache 2. 主要关注点 3. perf的使用 3.0 perf引入的overhead 3.1 perf list 3.2 ...

  5. 日常测试进行beans比较的简单方法

    日常测试进行beans比较的简单方法 摘要 想每天把有变化的bean抓取出来有新增的beans时能够及时进行分析和介入 保证beans 都是符合规范的. 方式和方法 开启actuator 打开bean ...

  6. Nginx 大并发 调优设置

    为了性能测试,放弃部分功能,保证绝对性能. 注意可能不能用于生产环境. 下面开始简单讲解. 1. worker_processes 工作线程数. 发现不用太多 一定不能多于操作系统的CPU核数. 2. ...

  7. Vue3类型判断和ref的两个作用

    1.类型判断的四种方法 isRef: 检查一个值是否为一个ref对象 isReactive:检查一个对象是否是由 reactive 创建的响应式代理 isReadonly: 检查一个对象是否是由 re ...

  8. golang: 模仿 VictoriaMetrics 中的做法,通过把局部变量放在自定义 Context 对象中来做到hot path 的 0 alloc

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 使用 benchmark 压测过程中通常会出现这样的信息: ...

  9. 使用Lua做为MMOARPG游戏逻辑开发脚本的一点体会

    项目背景 目前在一个大型MMOARPG游戏中使用Lua做为逻辑开发语言,Lua占整体代码量的80%. 我们这个MMO游戏开发近2年,客户端8人,项目组总体人数在100人(美术占70%),目前代码量很大 ...

  10. 手撕Vue-编译模板数据

    经上一篇编译指令数据后,我们已经可以将指令数据编译成具体需要展示的数据了,上一篇只是编译了指令数据,还没有编译模板数据,这一篇我们就来编译模板数据. 也就是 {{}} 这种模板的形式我们该如何编译,其 ...