Pinia

介绍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.起步 安装

yarn add pinia
 
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

1.新建一个文件夹store

2.新建文件[name].ts

3.定义仓库Store

import { defineStore } from 'pinia'

// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore('main', {
    // other options...
})

这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools。 将返回的函数命名为 use... 是跨可组合项的约定,以使其符合你的使用习惯。

使用 store

import { useStore } from '@/stores/counter'

export default {
    setup() {
        const store = useStore()

        return {
            // 您可以返回整个 store 实例以在模板中使用它
            store,
        }
    },
}

State

大多数时候,state 是 store 的核心部分。 我们通常从定义应用程序的状态开始。 在 Pinia 中,状态被定义为返回初始状态的函数。 Pinia 在服务器端和客户端都可以工作。

import { defineStore } from 'pinia'

const useStore = defineStore('storeId', {
    // 推荐使用 完整类型推断的箭头函数
    state: () => {
        return {
            // 所有这些属性都将自动推断其类型
            counter: 0,
            name: '浩然',
        }
    },
})

访问及使用 “state”

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

<template>
<div>pinia:{{countStore.current}}----{{countStore.name}}</div>
<div><button @click="change">pinia++</button></div>
</template>

<script setup lang="ts">
    import { ref, reactive } from 'vue'
    import { useCountStore } from "./store/count";
    const countStore = useCountStore()
    // 可以直接更改state的值
    const change = () => {
        countStore.current++
    }
</script>

重置状态

将状态 重置 到其初始值

const store = useStore()

store.$reset()

批量改变状态

countStore.$patch({
    current: ++countStore.current,
    name : '批量更改'
})

批量修改函数形式

countStore.$patch((state) => {
    state.current++
    state.name = '我被改变了'
})

替换state

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

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

countStore.$state = {
    current: 5,
    name: '修改整个state'
}

通过actions修改state

import {defineStore} from 'pinia'
export const useCountStore = defineStore('count',{
    state: () => {
        return {
            current: 1,
            name: '浩然'
        }
    },
    actions: {
        addCurrent(num) {
            this.current += num
        }
    }
})

使用

<template>
    <div>pinia:{{countStore.current}}----{{countStore.name}}</div>
    <div><button @click="change">pinia++</button></div>
</template>

<script setup lang="ts">
    import { useCountStore } from "./store/count";
    const countStore = useCountStore()
    const change = () => {
        countStore.addCurrent(2)
    }
</script>

订阅状态

通过 store 的 $subscribe() 方法查看状态及其变化,类似于 Vuex 的 subscribe 方法

与常规的 watch() 相比,使用 $subscribe() 的优点是 订阅 只会在 patches 之后触发一次

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

解构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 使其变为响应式对象

下载

Actions异步操作

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>
<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>
 

订阅Actions的调用

$onAction

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

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

getters

import { defineStore } from 'pinia'
export const useCountStore = defineStore('count', {
    state: () => {
        return {
            current: 1,
            name: '浩然'
        }
    },
    getters: {
        getName(): string {
            return 'getters-' + this.name
        }
    },
})

使用:

<template>
<div>{{countStore.getName}}</div>
</template>

<script setup lang="ts">
    import { useCountStore } from "./store/count";
    const countStore = useCountStore()
</script>

getters相互调用

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

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"

}))
posted @ 2022-10-20 08:52  d浩然  阅读(90)  评论(0编辑  收藏  举报