学习pinia

目录

认识pinia

1. 起步 安装

 2. 引入注册

3. 初始化仓库store

使用pinia---state

1. 直接修改

2. 批量修改state的值

3. 批量修改函数形式

4. 通过原始对象修改整个实例

5. 通过actions来进行修改

6. 解构state

使用pinia---getters,actions

1. actions

2. getters

pinia的一些api

1. $reset 

2. 订阅state的改变

 3.订阅Actions的调用

持久化存储


认识pinia

pinia是一个全局状态管理工具

Pinia.js具有以下特点

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

1. 起步 安装

yarn add pinia
 
npm install pinia

 2. 引入注册

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import {createPinia} from "pinia";

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

app.use(ElementPlus)
app.use(pinia)

app.mount('#app')

3. 初始化仓库store

需要以下几步

1.新建一个文件夹Store

2.新建文件[name].ts

3.定义仓库Store

import { defineStore } from 'pinia'

4.存储是使用定义的defineStore(),并且它需要一个唯一的名称,作为第一个参数传递

我们定义了一个枚举去做这个唯一值

5.定义值

State 箭头函数 返回一个对象 在对象里面定义值

getters 类似于computed

actions 可以提交一些同步或异步的方法来改变state

import {defineStore} from "pinia";
import {Namespace} from "./namespace.ts";

export const useStore = defineStore(Namespace.TEST, {
    state: () => {
        return {
            count: 0
        }
    },
    getters: {
        
    },
    actions: {
        
    }
});

使用pinia---state

在vue中需要调用导出的pinia 调用这个useStore函数,拿到他的返回值对象即可进行使用

<script setup lang='ts'>
  import {useStore} from "./store";
  const Test = useStore()
  console.log(Test.count)
</script>

<template>

</template>

<style scoped>

</style>

修改state值的方式

1. 直接修改

<script setup lang='ts'>
  import {useStore} from "./store";
  const Test = useStore()
</script>

<template>
  <button @click="Test.count++">增加</button> 
  {{Test.count}}----{{Test.name}}
</template>

<style scoped>

</style>

2. 批量修改state的值

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

$patch传入一个对象,进行修改

<script setup lang='ts'>
  import {useStore} from "./store";
  const Test = useStore()
  function change() {
    Test.$patch({
      count: 10,
      name: '张三'
    })
  }
</script>

<template>
  <button @click="Test.count++">增加</button>
  <button @click="change">修改</button>
  {{Test.count}}----{{Test.name}}
</template>

<style scoped>

</style>

3. 批量修改函数形式

$patch传入一个回调,进行修改,回调上面有一个参数state,公告state去修改,主要的目的就是为了带一下条件的去修改值

<script setup lang='ts'>
  import {useStore} from "./store";
  const Test = useStore()
  function change() {
    Test.$patch((state) => {
      if(state.count < 10) {
        state.count = 10
      }
    })
  }
</script>

<template>
  <button @click="Test.count++">增加</button>
  <button @click="change">修改</button>
  {{Test.count}}----{{Test.name}}
</template>

<style scoped>

</style>

4. 通过原始对象修改整个实例

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

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

<script setup lang='ts'>
  import {useStore} from "./store";
  const Test = useStore()
  function change() {
    Test.$state = {
      count: 10,
      name: '张三'
    }
  }
</script>

<template>
  <button @click="Test.count++">增加</button>
  <button @click="change">修改</button>
  {{Test.count}}----{{Test.name}}
</template>

<style scoped>

</style>

5. 通过actions来进行修改

import {defineStore} from "pinia";
import {Namespace} from "./namespace.ts";

export const useStore = defineStore(Namespace.TEST, {
    state: () => {
        return {
            count: 0,
            name: 'jjs'
        }
    },
    getters: {

    },
    actions: {
        changeState(num:number) {
            this.count += num;
        }
    }
});
<script setup lang='ts'>
  import {useStore} from "./store";
  const Test = useStore()
  function change() {
    Test.$state = {
      count: 10,
      name: '张三'
    }
  }
  function add(num:number) {
    Test.changeState(num)
  }
</script>

<template>
  <button @click="add(10)">增加</button>
  <button @click="change">修改</button>
  {{Test.count}}----{{Test.name}}
</template>

<style scoped>

</style>

6. 解构state

let {count,name} = Test.$state

像这样直接去结构的话,就会失去响应式。数据就不是响应式的了

解决方案可以使用 storeToRefs

storeToRefs原理: 像使用toRaw将proxy对象转换为原对象,然后通过循环给属性都调用toRefs方法将属性都转为ref对象,这样解构出来的属性值就是响应式的了。

<script setup lang='ts'>
  import {useStore} from "./store";
  import {storeToRefs} from "pinia";
  const Test = useStore()
  let {count,name} = storeToRefs(Test)
  function change() {
    Test.$state = {
      count: 10,
      name: '张三'
    }
  }
  function add(num:number) {
    Test.changeState(num)
  }
</script>

<template>
  <div>
    <button @click="add(10)">增加</button>
    <button @click="change">修改</button>
  </div>
  <p>{{Test.count}}----{{Test.name}}</p>
  <p>{{name}}----{{count}}</p>
</template>

<style scoped>

</style>

使用pinia---getters,actions

1. actions

可以支持同步,异步和相互调用

下面有一个案例来说明:

import {defineStore} from "pinia";
import {Namespace} from "./namespace.ts";

type User = {
    name: string,
    age: number
}
const Login = ():Promise<User> => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({
                name: 'jjs',
                age: 18
            })
        },2000)
    })
}
export const useStore = defineStore(Namespace.TEST, {
    state: () => {
        return {
            count: 0,
            name: 'jjs',
            user: <User>{}
        }
    },
    getters: {

    },
    actions: {
        changeStateCount(num:number) {
            this.count += num;
        },
        changeName(){
            this.name = 'jjs2'
        },
        async login() {
            let result = await Login();
            console.log(result)
            this.user = result;
            this.changeName()
        }
    }
});
<script setup lang='ts'>
  import {useStore} from "./store";
  const store = useStore();
  store.login()
</script>

<template>
 <p>{{store.user}}</p>
  <p>{{store.name}}</p>
</template>

<style scoped>

</style>

2. getters

getters: {
        newName(state) {
            return state.name + 'new'
        },
        doubleCount: (state) => {
            return state.count  + state.newName
        }
    },
<script setup lang='ts'>
  import {useStore} from "./store";
  const store = useStore();
  store.login()
</script>

<template>
 <p>{{store.user}}</p>
  <p>{{store.name}}</p>
  <p>{{store.doubleCount}}</p>
  <button @click="store.count++">++</button>
</template>

<style scoped>

</style>

pinia的一些api

1. $reset 

重置state到初始状态

<script setup lang='ts'>
  import {useStore} from "./store";
  const store = useStore();
  store.login()
</script>

<template>
 <p>{{store.user}}</p>
  <p>{{store.name}}</p>
  <p>{{store.doubleCount}}</p>
  <button @click="store.count++">++</button>
  <div>
    <button @click="store.$reset()">重置</button>
  </div>
</template>

<style scoped>

</style>

2. 订阅state的改变

类似于Vuex 的abscribe  只要有state 的变化就会走这个函数

store.$subscribe((state, getters) => {
        console.log(state, getters)
  })

 3.订阅Actions的调用

只要有actions被调用就会走这个函数

store.$onAction(({name, store, after, onError}) => {
    console.log(name, store, after, onError)
  })

持久化存储

pinia 和 vuex 都有一个通病 页面刷新状态会丢失

我们可以写一个pinia 插件缓存他的值

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"
 
}))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值