vuex状态管理几种状态(Vue中的Pinia状态管理工具 | 一篇文章教会你全部使用细节)
Pinia状态管理
Pinia和Vuex的对比
Pinia(发音为/piːnjʌ/ ,如英语中的“peenya ”)是最接近piña(西班牙语中的菠萝)的词;
Pinia开始于大概2019年 ,最初是作为一个实验为Vue重新设计状态管理 ,让它用起来适合组合式API(Composition API) 。
从那时到现在 ,最初的设计原则依然是相同的 ,并且目前同时兼容Vue2 、Vue3 ,也并不要求你使用Composition API;
Pinia本质上依然是一个状态管理的库 ,用于跨组件 、页面进行状态共享(这点和Vuex 、Redux一样);
那么我们不是已经有Vuex了吗?为什么还要用Pinia呢?
Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子 ,结合了 Vuex 5 核心团队讨论中的许多想法;
最终 ,团队意识到Pinia已经实现了Vuex5中大部分内容,所以最终决定用Pinia来替代Vuex;
与 Vuex 相比 ,Pinia 提供了一个更简单的 API ,具有更少的仪式,提供了 Composition-API 风格的 API;
最重要的是 ,在与 TypeScript 一起使用时具有可靠的类型推断支持;
和Vuex相比 ,Pinia有很多的优势:
优势一: mutations 不再存在:
他们经常被认为是非常冗长; 他们最初带来了 devtools 集成,但这不再是问题;优势二: 更友好的TypeScript支持 ,Vuex之前对TS的支持很不友好;
优势三: 不再有modules的嵌套结构:
你可以灵活使用每一个store ,它们是通过扁平化的方式来相互使用的;优势四: 也不再有命名空间的概念 ,不需要记住它们的复杂关系;
Pinia基本使用
🍤创建Pinia
使用Pinia之前 ,我们需要先对其进行安装:
yarn add pinia
npm install pinia
使用pinia我们需要在单独的js文件中创建一个pinia, 并且在main.js中将其注册, 如下:
这样我们项目中就已经存在pinia了
import { createPinia } from "pinia"; // 创建pinia const pinia = createPinia() // 导出pinia export default pinia import { createApp } from vue import App from ./App.vue // 导入pinia import pinia from ./stores const app = createApp(App) // 注册pinia app.use(pinia) app.mount(#app)🍤创建Store
什么是Store?
一个 Store (如 Pinia)是一个实体 ,它会持有为绑定到你组件树的状态和业务逻辑 ,也就是保存了全局的状态;
它有点像始终存在 ,并且每个人都可以读取和写入的组件;
你可以在你的应用程序中定义任意数量的Store来管理你的状态;
Store有三个核心概念(接下来会一一讲到):
state 、getters 、actions;
等同于组件的data 、computed 、methods;
一旦 store 被实例化 ,你就可以直接在 store 上访问 state 、getters 和 actions 中定义的任何属性;
定义一个Store:
Store 是使用 defineStore() 定义的, 我们一般都会在一个单独的js的文件创建store, 不同组件的数据, 我们会定义在不同的js文件中创建不同的store
由于pinia中可以定义多个store, 所以每一个store它都需要一个唯一名称 ,作为第一个参数传递;
// 定义关于counter的store import { defineStore } from "pinia" // 调用defineStore定义store, defineStore返回一个函数 const useCounter = defineStore("counter", { state: () => ({ counter: 101 }) }) // 将useCounter函数导出 export default useCounter第一个参数 name,也称为 id ,是必要的 ,Pinia 使用它来将 store 连接到 devtools 。
defineStore()返回的函数统一使用useXXX作为命名方案, 且XXX一般就使用传入的id,这是约定的规范;
调用defineStore()返回的函数才会创建store
Store在它被使用之前是不会创建的 ,我们可以通过调用use函数来使用Store:
<template> <!-- 展示counterStore.counter的状态 --> <h2>{{ counterStore.counter }}</h2> </template> <script setup> // 导入我们自定义关于counter的store import useCounter from ../stores/counter; // 调用函数才会创建store, 不调用不会创建 const counterStore = useCounter() </script>注意Store获取到后, 如果我们想要对其解构, 不能直接解构 ,直接解构的话会失去响应式:
为了从 Store 中提取属性同时保持其响应式我们有两种方式
方式一: 解构时包裹一层toRefs
方式二: pinia给我们提供了一个方法, 使用storeToRefs()方法可以保持数据的响应式 。
方式一
<script setup> import { toRefs } from vue; import useCounter from ../stores/counter; const counterStore = useCounter() // 包裹一层toRefs const { counter } = toRefs(counterStore) </script>方式二
<script setup> import { storeToRefs } from pinia; import useCounter from ../stores/counter; const counterStore = useCounter() // 包裹一层storeToRefs const { counter } = storeToRefs(counterStore) </script>Pinia核心概念State
🍟state基本使用
state 是 store 的核心部分,因为store是用来帮助我们管理状态的 。
在 Pinia 中 ,状态被定义为返回初始状态的函数;
前面我们创建了一个counter.js文件用于定义counter的store, 接下来我们创建一个urse.js文件, 定义一个用户信息的store来演示state
在pinia中state和vuex中一样, state是一个函数, 返回一个对象
import { defineStore } from "pinia" const useUser = defineStore("user", { // state定义状态 state: () => ({ name: "chenyq", age: 18, height: 1.88 }) }) export default useUser将定义的Store展示到组件中
<template> <div class="home"> <!-- 展示userStore中的状态 --> <h2>{{ name }}</h2> <h2>{{ age }}</h2> <h2>{{ height }}</h2> </div> </template> <script setup> import { storeToRefs } from pinia; // 导入我们自定义的store import useUser from "../stores/user" // 调用函数创建store const userStore = useUser() // 将store中的状态解构出来 const { name, age, height } = storeToRefs(userStore) </script>🍟state其他操作
读取和写入 state
默认情况下 ,您可以通过 store 实例访问状态来直接读取, 刚刚我们就是这样读取状态的
写入状态其实也同理, 通过store实例访问状态直接修改
<template> <div class="home"> <!-- 展示userStore中的状态 --> <h2>{{ name }}</h2> <h2>{{ age }}</h2> <h2>{{ height }}</h2> <button @click="changeInfo">修改信息</button> </div> </template> <script setup> import { storeToRefs } from pinia; import useUser from "../stores/user" const userStore = useUser() const { name, age, height } = storeToRefs(userStore) function changeInfo() { // 使用实例访问状态, 进行修改 userStore.name = "王老五" userStore.age = 20 userStore.height = 1.89 } </script>重置 State
当我们对某些状态进行了修改之后, 我们可以通过调用 store 上的 $reset() 方法将状态 重置到其初始值;
$reset()方法会将所有的状态重置到初始值
<template> <div class="home"> <!-- 展示userStore中的状态 --> <h2>{{ name }}</h2> <h2>{{ age }}</h2> <h2>{{ height }}</h2> <button @click="changeInfo">修改信息</button> <button @click="resetInfo">重置信息</button> </div> </template> <script setup> import { storeToRefs } from pinia; import useUser from "../stores/user" const userStore = useUser() const { name, age, height } = storeToRefs(userStore) function changeInfo() { userStore.name = "王老五" userStore.age = 20 userStore.height = 1.89 } function resetInfo() { // 重置状态 userStore.$reset() } </script>同时修改多个状态
可以调用 $patch 方法 , 它允许您使用部分“state ”对象同时应用多个更改;
<template> <div class="home"> <!-- 展示userStore中的状态 --> <h2>{{ name }}</h2> <h2>{{ age }}</h2> <h2>{{ height }}</h2> <button @click="changeInfo">修改信息</button> </div> </template> <script setup> import { storeToRefs } from pinia; import useUser from "../stores/user" const userStore = useUser() const { name, age, height } = storeToRefs(userStore) function changeInfo() { // $patch一次性修改多个状态 userStore.$patch({ name: "罗三炮", age: 50, height: 1.58 }) } </script>Pinia核心Getters
🍕getters基本使用
Getters相当于Store的计算属性:
它们可以用 defineStore() 中的 getters 属性定义;
getters中可以定义接受一个state作为参数的函数;
在defineStore中定义getters import { defineStore } from "pinia" const useCounter = defineStore("counter", { state: () => ({ counter: 101 }), // 定义getters getters: { doubleCounter(state) { return state.counter * 2 } } }) export default useCounter 直接通过store对象就可以访问当前store的Getters <template> <!-- 访问当前store的Getters --> <h2>{{ counterStore.doubleCounter }}</h2> </template> <script setup> import useCounter from "../stores/counter" const counterStore = useCounter() </script>🍕getters其他操作
Getters中访问自己的其他Getters
我们可以通过this来访问到当前store实例的所有其他属性;
this相当于是绑定的store实例
例如在getter中访问自己的doubleCounter getters: { doubleCounter(state) { return state.counter * 2 }, doubleCounterAddOne() { return this.doubleCounter + 1 } }Getters也可以返回一个函数 ,这样就可以接受参数:
const useCounter = defineStore("counter", { state: () => ({ counter: 101, friend: [ {id: 111, name: "chenyq"}, {id: 112, name: "王老五"}, {id: 113, name: "罗三炮"}, ] }), getters: { // getter可以返回一个函数 getfriendById() { return (id) => { return this.friend.find(item => item.id == id) } } } }) <h2>{{ counterStore.getfriendById(111) }}</h2> <h2>{{ counterStore.getfriendById(112) }}</h2>当前Getters访问其他store中的state/getters
// 导入usrUser import useUser from "./user" const useCounter = defineStore("counter", { state: () => ({ counter: 101 }), getters: { showMessage(state) { // 拿到userStore对象, 获取userStore中的信息 const userStore = useUser() // 返回自己store的信息拼接上userStore中的信息 return `${state.counter}${userStore.name}` } } }) <h2>{{ counterStore.showMessage }}</h2>Pinia核心Actions
🥧Actions基本使用
Actions 相当于组件中的 methods 。
可以使用 defineStore() 中的 actions 属性定义 ,并且它们非常适合定义业务逻辑;
和getters一样 ,在action中可以通过this访问整个store实例的所有操作;
const useCounter = defineStore("counter", { state: () => ({ counter: 101 }), actions: { increment() { this.counter++ } } }) <h2>{{ counterStore.counter }}</h2> <button @click="changeState">+1</button> <script setup> import useCounter from "../stores/counter" const counterStore = useCounter() function changeState() { // 通过store实例调用即可 counterStore.increment() } </script>🥧Actions异步操作
Actions中是支持异步操作的 ,并且我们可以编写异步函数 ,在函数中使用await
例如在Actions发生网络请求
import { defineStore } from pinia const useHome = defineStore("home", { state: () => ({ // 定义空数组用于接收网络请求数据 banners: [], recommends: [] }), actions: { // 支持异步操作 async fetchHomeMultidata() { // 发送网络请求获取数据 const res = await fetch("http://123.207.32.32:8000/home/multidata") const data = await res.json() // 将获取的数据添加到state中 this.banners = data.data.banner.list this.recommends = data.data.recommend.list } } }) export default useHome展示网络请求获取到homeStore中的数据
<template> <div class="about"> <ul v-for="item in homeStore.banners" :key="item.acm"> <li>{{ item.title }}</li> </ul> </div> </template> <script setup> import useHome from "../stores/home" const homeStore = useHome() // 告知发送网络请求 homeStore.fetchHomeMultidata() </script>创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!