vue3笔记十七(Pinia)

1、pinia简介

全局状态管理工具

Pinia.js 有如下特点:

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

官方文档Pinia
git 地址 https://github.com/vuejs/pinia

2、安装及注册

2.1、安装

yarn add pinia
或
npm install pinia

2.2、注册

// 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)
app.mount('#app')

3、初始化Store仓库

1、src下新建store文件夹
2、新建一个[name].ts文件
3、在新建的[name].ts文件下定义仓库
4、新建一个store-namespace/index.ts文件用于名称管理

// store-namespace/index.ts
export const enum Names {
  User= 'USER'
}
// User.ts
import { defineStore } from 'pinia'
import { Names } from './store-namespace'

// defineStore需要一个唯一的名称,相当于id,作为第一个参数传递
// defineStore将返回的函数一般命名为use...
export const useUserInfo = defineStore(Names.User, {
  // state在vuex中是一个对象
  //      在pinia中为一个箭头函数返回一个对象,在对象里面定义数据
  state: () => ({
    name: '张三1111',
    age: 20
  }),
  // 类似于computed
  getters: {},
  // pinia中actions既可以操作异步也能同步操作state值
  actions: {
    setData() {
      this.name = '杨七'
      this.age++
    }
  }
})

4、pinia中state值修改的五种方式

<template>
  <div>
    <h1>
      pinia-----{{User.name}}------{{User.age}}
    </h1>
    <button @click="change">change</button>
  </div>
</template>

<script setup lang="ts">
import { useUserInfo } from '@/store/User.ts'

const User = useUserInfo()

// 方式一:可以直接修改值,不同于vuex
// const change = () =>  {
//   User.name = '哈哈'
//   User.age++
// }

// 方式二:$patch方法对象形式修改
// const change = () =>  {
//   User.$patch({
//     name: '李四',
//     age: 100
//   })
// }

// 方式三:$patch方法函数形式修改,内部可自定义逻辑修改
// const change = () =>  {
//   User.$patch((state) => {
//     state.name = '王五'
//     state.age++
//   })
// }

// 方式四:$state方法设置新对象替换整个store,必须修改整个state对象的所有属性
// (但本人发现修改单个属性也可以,并无其他影响)
// const change = () =>  {
//   User.$state = {
//     name: '赵六',
//     age: 999,
//   }
// }

// 方式五:通过actions修改state值
const change = () =>  {
  User.setData()
}
</script>

5、解构pinia中state值

<template>
  <div>
    <h4>
      原始值-----{{User.name}}------{{User.age}}
    </h4>
    <h4>
      解构值-----{{name}} ----- {{age}}
    </h4>
    <button @click="change">change</button>
  </div>
</template>

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

const User = useUserInfo()

// 1、pinia值直接解构,失去响应式
// const { name, age } = User

// 2、使用storeToRefs方法解构数据为响应式
const { name, age } = storeToRefs(User)

const change = () =>  {
  User.age++  // 或 age.value++
}

</script>
// storeToRefs源码
// storeToRefs同toRefs一样,给内部数据包裹一层toRef

function storeToRefs(store) {
    // See https://github.com/vuejs/pinia/issues/852
    // It's easier to just use toRefs() even if it includes more stuff
    if (isVue2) {
        // @ts-expect-error: toRefs include methods and others
        return toRefs(store);
    }
    else {
    	// 首先将store响应式对象转化为普通对象,防止下面的toRef重复代理
        store = toRaw(store);
        // 最终return 这个 refs
        const refs = {};
        for (const key in store) {
            const value = store[key];
            // 判断属性值是否是ref或reactive响应式对象,如果是拷贝到refs中,将其原始对象包裹toRef变为响应式对象
            if (isRef(value) || isReactive(value)) {
                // @ts-expect-error: the key is state or getter
                refs[key] =
                    // ---
                    toRef(store, key);
            }
        }
        return refs;
    }
}

store转化为普通对象后的属性展示
在这里插入图片描述

6、actions、getters使用

actions既支持异步也支持同步方法

import { defineStore } from 'pinia'
import { Names } from './store-namespace'

type Result = {
  name: string,
  age: number
}

const Login = ():Promise<Result> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        name: '王翠花',
        age: 19
      })
    }, 3000)
  })
}

export const useUserInfo = defineStore(Names.User, {
  state: () => ({
    num: 100,
    user: <Result>{}
  }),
  actions: {
    // 异步方法
    async getLogin() {
      let res = await Login()
      this.user = res
      console.log(res)
      
      // actions中相互调用
      this.setNum(res.age)
    },
    
    // 同步方法
    setNum(val: number) {
      this.num = val
    },
  }
})

getters使用

import { defineStore } from 'pinia'
import { Names } from './store-namespace'

export const useUserInfo = defineStore(Names.User, {
  state: () => ({
    num: 100,
  }),
  getters: {
  	// 写法1 箭头函数不能使用this,this指向已经变成undefined,修改请用state
    doubleNum: (state) => state.num * 2,
    
    // 写法2 普通函数形式中可使用this,但ts无法进行正确的类型推导,需定义返回值类型
    tripleNum(): number {
      return this.num * 3
    },
    
    // getters中相互调用
    numStr1(): string {
      return this.doubleNum + 'str'
    },
  },
})

7、pinia API

$reset、$subscribe、$onAction
<template>
  <div>
    <h4>
      actions-----{{User.info}} ---- {{User.num}}
    </h4>
    <button @click="change">change</button>
    <button @click="reset">reset</button>
    <button @click="actionChange">actionChange</button>
  </div>
</template>

<script setup lang="ts">
import { useUserInfo } from '../store'

const User = useUserInfo()

const change = () =>  {
  User.info.name = '张三'
  User.info.age = 999
  User.num++
}

const reset = () => {
  // $reset重置store到初始状态,将state所有值重置回初始状态
  User.$reset()
}

const actionChange = () =>  {
  User.setNum(789)
}

// 监听state值的改变,只要state值改变都会走这个函数
User.$subscribe((args, state) => {
  console.log(args, state, 'state数据改变----')
}, {
  // 组件销毁后仍可监听
  detached: true
})

// 当actions中函数被调用,触发监听
User.$onAction((args) => {
  console.log(args, 'actions调用--------------')
})
</script>

8、pinia 数据持久化插件

8.1、下载插件

npm i pinia-plugin-persist --save
或
yarn add pinia-plugin-persist

8.2、在main.ts中引入

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 持久化插件
import piniaPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPersist)

const app = createApp(App)
app.use(store)
app.mount('#app')

8.3、模块中使用

import { defineStore } from 'pinia'
import { Names } from './store-namespace'

type Result = {
  name: string,
  age: number
}

export const useUserInfo = defineStore(Names.User, {
  state: () => ({
    info: <Result>{},
    num: 100,
  }),
  persist: {
    enabled: true, // 开启存储
    // strategies 设置存储位置及存储变量,不写默认为sessionStorage中存储所有数据
    strategies: [
      // storage 设置存储到localStorage或sessionStorage中
      // paths 不写则默认存储所有数据,写了只存储指定变量数据(如下只对num做了数据持久化)
      {storage: localStorage, paths: ['num']}
    ]
  },
  getters: {},
  actions: {},
})

原文链接:
https://xiaoman.blog.csdn.net/article/details/123338137

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值