学习pinia 介绍-State-Getters-Actions-Plugins

可爱小菠萝🍍,安排一下!
在这里插入图片描述
内容基本参考官网:
https://pinia.vuejs.org
(官网的字体是Dank mono 不知道有没有跟我一样很喜欢的朋友😢 已经美美换上



介绍

pinia:最新一代的轻量级状态管理插件
如果你之前使用过 vuex 进行状态管理的话,那么 pinia 就是一个类似的插件

优点:

  • 完整的ts支持
  • 轻量
  • 去除了mutations,只有state、getters、actions

安装

npm i pinia

引入注册

// main.ts
import { createApp } from 'vue'
import App from './App.vue'

import {createPinia} from 'pinia'

const app = createApp(App);

const store = createPinia();
app.use(store);

初始化仓库Store

1.根目录下新建store文件夹,再建一个index.ts
2.main.ts
store是用defineStore()定义的
第一个参数传一个唯一的名称
第二个参数还可以传一些别的比如getters、actions之类的,后面再说

import { defineStore } from "pinia";

export const useUserStore = defineStore('user', {
  state: () => ({
    // 初始化的值
    name: 'AIpoem',
    age: 20
  }),
  actions: {
    changeInfo() {
      this.name  = 'change poem'
    },
  },
})

3.在组件中使用
实例化store之后,可以直接在store上访问任何定义在stategettersactions的属性
store是经过reactive包装的对象,这意味着我们不需要写.value,但是就类似于props我们是不能解构store的

<script setup lang="ts">
import { useUserStore } from "./store";
const userStore = useUserStore();

// ❌❌❌ 这样会丢失响应性
const { name, age } = userStore;
</script>

<template>
  {{ userStore.name }},{{ userStore.age }}
</template>

如果需要从store中提出属性,同时保持其响应性,我们要用storeToRefs()
它会为每一个属性都包上一层ref(会跳过action和任何不具有响应性的属性

并且,action我们可以直接解构,它也是绑定在store身上的

<script setup lang="ts">
import { useUserStore } from "../store";
import { storeToRefs } from "pinia";

const userStore = useUserStore();
const { name, age } = storeToRefs(userStore);
const { changeInfo } = userStore;
</script>

State

访问State

可以通过store实例直接对state进行读和写

const userStore = useUserStore();
userStore.name = 'change poem';

重置State

可以调用store实例上的$reset()方法将state重置为初始值

const userStore = useUserStore();
console.log(userStore.name);  // AIpoem
userStore.name = "change poem";

userStore.$reset();			 
console.log(userStore.name);  // AIpoem

改变State

除了用store实例直接改变StateuserStore.name = "change poem"),也可以调用$patch()方法
使用$patch()方法可以批量修改state

1.$patch()传一个对象
允许你对state进行部分更改

const userStore = useUserStore();
userStore.$patch({
  name: "change poem",
})

2.$patch()传一个函数
但是当state中有数组的时候,上面那种传对象的方法就不太好用了,比如数组要新增、要删除一个元素,我们都要创建一个新数组来更新
那这种时候传函数就更方便

// 我上面的例子state中没有数组,这里就加一个数组演示一下
// state: () => ({
// 	// 初始化的值
// 	name: "AIpoem",
// 	age: 20,
// 	hobby: ["吃", "喝", "玩", "乐"],
// }),
const userStore = useUserStore();
// 这个state就是store实例上的state
userStore.$patch(state => {
  state.hobby.push("睡")
})
// 传对象写法
// userStore.$patch({
//   hobby: ["吃", "喝", "玩", "乐", "睡"]
// })

替换State

你可以给store实例的$state属性赋值一个新对象,这样能替换掉整个state

const userStore = useUserStore();
userStore.$state = {
  name: "change poem",
  age: 120,
  hobby: ["吃", "喝", "玩", "乐", "睡"],
};

订阅状态

你可以通过store实例的$subscribe()方法来监视state的变化
$subscribe()有两个参数:
第一个参数是一个回调函数:

const userStore = useUserStore();
userStore.$subscribe((mutation, state) => {
  // 可能值:‘direct’ 'patch object' 'patch function'
  console.log(mutation.type) 		// 修改的方式
  console.log(mutation.storeId)		// defineStore()时传的名字
})

第二个参数是固定值: { detached: true }
组件卸载的时候,state的订阅也会自动卸载,如果我们希望订阅在组件卸载后还能保留,那就传第二个参数

const userStore = useUserStore();
userStore.$subscribe((mutation, state) => {
  ...
}, { detached: true })

Getters

相当于store实例的computed,用来修饰值,并且是有缓存的

如果只需要用到state,直接用就行

export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'AIpoem',
  }),
  getters: {
    sayName: state => `我的名字是${state.name}`
  }
})

使用this

如果需要用到其他getter,我们可以通过this来访问store实例
使用了this的注意点:
1.用了this就不能用箭头函数,否则this不指向store实例
2.在ts中有必要定义返回值类型

export const useUserStore = defineStore('user', {
  state: () => ({
    // 初始化的值
    name: 'AIpoem',
  }),
  getters: {
    sayName: state => `我的名字是${state.name}`,
    sayHi():string {
      return `你好, ${this.sayName}`
    }
  }
})

模板中使用,就跟用computed一样

<script setup lang="ts">
import { useUserStore } from "../store";
const userStore = useUserStore();
</script>

<template>
	{{ userStore.sayName }} <br />
	{{ userStore.sayHi }}
</template>

在这里插入图片描述

传参

getters的传参方法也跟computed一样,就是return一个function,用这个function来接收参数

export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'AIpoem',
  }),
  getters: {
    isMyName: state => {
      return function(userName: string):boolean {
        return state.name === userName
      }
    }
    // 简写
    // isMyName: state => (userName: string) => state.name === userName
  }
})

模板中直接传参就行:

<script setup lang="ts">
import { useUserStore } from "../store";
const userStore = useUserStore();
</script>

<template>
  {{ userStore.isMyName("AIpoem") }} <br />
  {{ userStore.isMyName("111") }}
</template>

在这里插入图片描述

⚠️:如果getters返回了一个函数,它将不再缓存


Actions

相当于store实例的methods
支持同步也支持异步

同步

import { defineStore } from "pinia";

interface User {
  name: string,
  age: number
}
export const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: <User>{},
  }),
  actions: {
    setUserInfo() {
      this.userInfo = {
        name: 'AIpoem',
        age: 20
      }
    }
  }
})

异步

一般结合async await一起使用

import { defineStore } from "pinia";

interface User {
  name: string,
  age: number
}

const getInfo = (): Promise<User> => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        name: 'AIpoem',
        age: 20,
      })
    }, 3000)
  })
}

export const useUserStore = defineStore('user', {
  state: () => ({
    userInfo: <User>{},
  }),
  actions: {
    async setUserInfo() {
      this.userInfo = await getInfo()
    }
  }
})

订阅actions

可以使用store实例的$onAction()方法来监视actions的调用
$onAction()有两个参数:
第一个参数是一个回调函数:

userStore.$onAction(args => {
  // 调用的那个action的名字
  console.log(args.name); 
  // store实例	
  console.log(args.store); 
  // action成功调用并运行完触发
  args.after(() => {
    console.log("after");
  });
  // action抛错或返回一个失败的promise时触发
  args.onError(() => {
	console.log("出错了");
  });
});

第二个参数是固定值: true
组件卸载的时候,action的订阅也会自动卸载,如果我们希望订阅在组件卸载后还能保留,那就传第二个参数

userStore.$onAction(args => {
  // 调用的那个action的名字
  console.log(args.name); 
  // store实例	
  console.log(args.store); 
  // action成功调用并运行完触发
  args.after(() => {
    console.log("after");
  });
  // action抛错或返回一个失败的promise时触发
  args.onError(() => {
	console.log("出错了");
  });
}, true);

Plugins

我们可以通过写插件对piniastore进行一些扩展

和vue一样,通过pinia.use()注册插件

pinia的插件是一个函数
pinia内部会帮我们调用这个插件函数,并且传回来一个可选的参数context

( 这部分演示的是js的写法,如果是ts还需要加一些东西,放在更后面了

// main.js
import { createApp, markRaw } from 'vue'
import { router } from './router'
import { createPinia } from 'pinia'

const myPiniaPlugin = (context) => {
  // defineStore()时传进去的第二个参数
  console.log(context.options);
  // store实例
  console.log(context.store);
  
  // 可以给store实例添加一个全局属性
  context.store.globalProperty = '我是一个全局属性'
  
  // 可以给store实例添加一个新的state
  context.store.$state.newProperty = context.store.globalProperty;
  // $state可读可写
  console.log(context.store.$state);	// Proxy {newProperty: '我是一个全局属性'}

  // 添加第三方库的实例,需要用markRow()包裹,标记为不可响应式
  context.store.router = markRow(router)

  // 这两个订阅也可以调,在里面处理一些需要的逻辑
  context.store.$subscribe(() => {})
  context.store.$onAction(() => {})
}

const app = createApp(App)
const store = createPinia()

store.use(myPiniaPlugin)
app.use(store)

defineStore()时可以传第三个参数,他会出现在👆的context.options中,这样我们可以利用自定义的第三个参数来做一些事情

使用ts编写插件:
context的类型是PiniaPluginContext,这个要导进来写上

1.如果为store实例添加了新的属性
需要扩展PiniaCustomProperties接口

// main.ts
import 'pinia'
import {createPinia, PiniaPluginContext} from 'pinia'

declare module 'pinia' {
  export interface PiniaCustomProperties {
    globalProperty: string
  }
}

const myPiniaPlugin = (context: PiniaPluginContext) => {
  // 给store实例添加一个全局属性
  context.store.globalProperty = '我是一个全局属性'
}

2.如果给store实例添加了新的state
需要扩展PiniaCustomStateProperties接口

...
declare module 'pinia' {
  export interface PiniaCustomStateProperties {
    newProperty: string
  }
}

const myPiniaPlugin = (context: PiniaPluginContext) => {
  // 给store实例添加一个全局属性
  context.store.$state.newProperty = context.store.globalProperty;
}
...

3.如果添加了新的创建选项,也就是给defineStore()传了第三个参数
需要扩展DefineStoreOptionsBase接口

// 这我也不知道举什么例子了
declare module 'pinia' {
  export interface DefineStoreOptionsBase {
    ...
  }
}

(最后部分整的有点潦草我清楚我明白,原因是我还没有参透ts🙏)
(等我学成归来xx等我需要用到的时候再研究一下🙏)
(详情请咨询https://pinia.vuejs.org

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值