Pinia

简介

前言 全局状态管理工具

Pinia.js 有如下特点:

完整的 ts 的支持;
足够轻量,压缩后的体积只有1kb左右;
去除 mutations,只有 state,getters,actions;
actions 支持同步和异步;
代码扁平化没有模块嵌套,只有 store 的概念,store 之间可以自由使用,每一个store都是独立的
无需手动添加 store,store 一旦创建便会自动添加;
支持Vue3 和 Vue2
官方文档Pinia

git 地址 https://github.com/vuejs/pinia

1.起步 安装

npm install pinia

2.引入注册Vue3

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

Vue2 使用

import { createPinia, PiniaVuePlugin } from 'pinia'
 
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
 
new Vue({
  el: '#app',
  // other options...
  // ...
  // note the same `pinia` instance can be used across multiple Vue apps on
  // the same page
  pinia,
})

初始化

创建store文件夹
在store/index.ts下

import { defineStore } from 'pinia'

import { Names } from "./namespace/index";


/**
 * 参数一:唯一名称
 */
// 简单写法: export const useTestStore = defineStore('Name',{})

export const useTestStore = defineStore(Names.Test, {
  state: () => {
    return {
      name: '李四'
    }
  },
  //类似于computed 可以帮我们去修饰我们的值
  getters: {

  },
  //可以操作异步 和 同步提交state
  actions: {

  }

})

在store/namespace/index.ts下

export const enum Names {
  Test = 'TEST'
}

state使用

<!-- name -->
<template>
  <div>
    {{ Test.name }}
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, toRefs } from "vue";

import { useTestStore } from "./store/index";    // 引入

const Test = useTestStore();      // 
</script>

<style scoped lang="less"></style>

state修改值

1.State 是允许直接修改值的 例如current++

<template>
     <div>
         <button @click="Add">+</button>
          <div>
             {{Test.current}}
          </div>
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
    Test.current++
}
 
</script>

2.批量修改State的值
在他的实例上有$patch方法可以批量修改多个值

<template>
     <div>
         <button @click="Add">+</button>
          <div>
             {{Test.current}}
          </div>
          <div>
            {{Test.age}}
          </div>
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
    Test.$patch({
       current:200,
       age:300
    })
}
 
</script>
 

3.批量修改函数形式
推荐使用函数形式 可以自定义修改逻辑

<template>
     <div>
         <button @click="Add">+</button>
          <div>
             {{Test.current}}
          </div>
          <div>
            {{Test.age}}
          </div>
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
    Test.$patch((state)=>{
       state.current++;
       state.age = 40
    })
}
 
</script>
 

4.通过原始对象修改整个实例
$state您可以通过将store的属性设置为新对象来替换store的整个状态

缺点就是必须修改整个对象的所有属性

<template>
     <div>
         <button @click="Add">+</button>
          <div>
             {{Test.current}}
          </div>
          <div>
            {{Test.age}}
          </div>
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
    Test.$state = {
       current:10,
       age:30
    }    
}
 
</script>
 

5.通过actions修改
定义Actions

在actions 中直接使用this就可以指到state里面的值

import { defineStore } from 'pinia'
import { Names } from './store-naspace'
export const useTestStore = defineStore(Names.TEST, {
     state:()=>{
         return {
            current:1,
            age:30
         }
     },
 
     actions:{
         setCurrent () {
             this.current++
         }
     }
})

使用方法直接在实例调用

<template>
     <div>
         <button @click="Add">+</button>
          <div>
             {{Test.current}}
          </div>
          <div>
            {{Test.age}}
          </div>
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
     Test.setCurrent()
}
 
</script>

解构store

在Pinia是不允许直接解构是会失去响应性的

const Test = useTestStore()
 
 
const { current, name } = Test
 
console.log(current, name);

差异对比

修改Test current 解构完之后的数据不会变

而源数据是会变的

<template>
  <div>origin value {{Test.current}}</div>
  <div>
    pinia:{{ current }}--{{ name }}
    change :
    <button @click="change">change</button>
  </div>
</template>
  
<script setup lang='ts'>
import { useTestStore } from './store'
 
const Test = useTestStore()
 
const change = () => {
   Test.current++
}
 
const { current, name } = Test
 
console.log(current, name);
 
 
</script>

解决方案可以使用 storeToRefs

import { storeToRefs } from 'pinia'
 
const Test = useTestStore()
 
const { current, name } = storeToRefs(Test)

其原理跟toRefs 一样的给里面的数据包裹一层toref

源码 通过toRaw使store变回原始数据防止重复代理

循环store 通过 isRef isReactive 判断 如果是响应式对象直接拷贝一份给refs 对象 将其原始对象包裹toRef 使其变为响应式对象

action与getters使用

Actions(支持同步异步)

1.同步 直接调用即可

// store/index.ts
import { defineStore } from 'pinia'
import { Names } from './store-naspace'
export const useTestStore = defineStore(Names.TEST, {
    state: () => ({
        counter: 0,
    }),
    actions: {
        increment() {
            this.counter++
        },
        randomizeCounter() {
            this.counter = Math.round(100 * Math.random())
        },
    },
})
<template>
     <div>
         <button @click="Add">+</button>
          <div>
             {{Test.counter}}
          </div>    
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
     Test.randomizeCounter()
}
 
</script>
 

2.异步 可以结合async await 修饰

import { defineStore } from 'pinia'
import { Names } from './store-naspace'
 
type Result = {
    name: string
    isChu: boolean
}
 
const Login = (): Promise<Result> => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({
                name: '小满',
                isChu: true
            })
        }, 3000)
    })
}
 
export const useTestStore = defineStore(Names.TEST, {
    state: () => ({
        user: <Result>{},
        name: "123"
    }),
    actions: {
        async getLoginInfo() {
            const result = await Login()
            this.user = result;
        }
    },
})
template
<template>
     <div>
         <button @click="Add">test</button>
          <div>
             {{Test.user}}
          </div>    
     </div>
</template>
 
<script setup lang='ts'>
import {useTestStore} from './store'
const Test = useTestStore()
const Add = () => {
     Test.getLoginInfo()
}
 
</script>
 

3.多个action互相调用getLoginInfo setName

    state: () => ({
        user: <Result>{},
        name: "default"
    }),
    actions: {
        async getLoginInfo() {
            const result = await Login()
            this.user = result;
            this.setName(result.name)
        },
        setName (name:string) {
            this.name = name;
        }
    },

getters

1.使用箭头函数不能使用this this指向已经改变指向undefined 修改值请用state

主要作用类似于computed 数据修饰并且有缓存

    getters:{
       newPrice:(state)=>  `$${state.user.price}`
    },

2.普通函数形式可以使用this

    getters:{
       newCurrent ():number {
           return ++this.current
       }
    },

3.getters 互相调用

    getters:{
       newCurrent ():number | string {
           return ++this.current + this.newName
       },
       newName ():string {
           return `$-${this.name}`
       }
    },

API

  1. $ reset
    重置store到他的初始状态
state: () => ({
     user: <Result>{},
     name: "default",
     current:1
}),

Vue 例如我把值改变到了10

const change = () => {
     Test.current++
}

调用$reset();

将会把state所有值 重置回 原始状态

2.订阅state的改变
类似于Vuex 的abscribe 只要有state 的变化就会走这个函数

Test.$subscribe((args,state)=>{
   console.log(args,state);
   
})

第二个参数

如果你的组件卸载之后还想继续调用请设置第二个参数

Test.$subscribe((args,state)=>{
   console.log(args,state);
   
},{
  detached:true
})

3.订阅Actions的调用
只要有actions被调用就会走这个函数

Test.$onAction((args)=>{
   console.log(args);
   
})

pinia 常驻

方式一:
一、通过pinia-plugin-persist插件来进行pinia的数据持久化操作
二、使用步骤
1.首先下载安装插件
代码如下(示例):

npm i pinia-plugin-persist --save --include=dev
// 或者使用 yarn 安装
yarn add pinia-plugin-persist

2.在main.ts/main.js中引入
代码如下(示例):

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from "pinia"
// 数据持久化插件
import piniaPersist from 'pinia-plugin-persist'
const pinia = createPinia()
pinia.use(piniaPersist)
const app = createApp(App)
app.use(pinia)
app.mount('#app')
  1. 具体模块中的使用看下面的代码
import { defineStore } from "pinia";
export const useHome = defineStore('home', {
  state: ()=>{
    return {
      studyCourse: 0,
      num: 1,
      msg: '测试'
    }
  },
  persist: {
    enabled: true, // 开启存储
    // **strategies: 指定存储位置以及存储的变量都有哪些,该属性可以不写,不写的话默认是存储到sessionStorage里边,默认存储state里边的所有数据**
    strategies:[
      { storage: localStorage, paths: ['studyCourse', 'num']}
      // storage 存储到哪里 sessionStorage/localStorage
      // paths是一个数组,要存储缓存的变量,当然也可以写多个
      // paths如果不写就默认存储state里边的所有数据,如果写了就存储指定的变量
    ]
  },
  actions: {
    addStudyCourse () {
      this.studyCourse++
    }
  },
})

方式二:

const __piniaKey = '__PINIAKEY__'
//定义兜底变量
 
 
type Options = {
   key?:string
}
//定义入参类型
 
 
 
//将数据存在本地
const setStorage = (key: string, value: any): void => {
 
localStorage.setItem(key, JSON.stringify(value))
 
}
 
 
//存缓存中读取
const getStorage = (key: string) => {
 
return (localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key) as string) : {})
 
}
 
 
//利用函数柯丽华接受用户入参
const piniaPlugin = (options: Options) => {
 
//将函数返回给pinia  让pinia  调用 注入 context
return (context: PiniaPluginContext) => {
 
const { store } = context;
 
const data = getStorage(`${options?.key ?? __piniaKey}-${store.$id}`)
 
store.$subscribe(() => {
 
setStorage(`${options?.key ?? __piniaKey}-${store.$id}`, toRaw(store.$state));
 
})
 
//返回值覆盖pinia 原始值
return {
 
...data
 
}
 
}
 
}
 
 
//初始化pinia
const pinia = createPinia()
 
 
//注册pinia 插件
pinia.use(piniaPlugin({
 
key: "pinia"
 
}))

原文链接:https://blog.csdn.net/qq1195566313/article/details/123431769

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值