1、状态管理 (共享全局变量)
当 Vue 应用变得复杂时,多个组件之间可能需要共享和同步数据。如果每个组件都独立地管理自己的数据,会导致数据的不一致性、难以跟踪和维护。通过状态管理,可以将页面之间共享的全局变量(例如:用户信息、购物车数据、应用的配置等)集中存储在一个地方,使得各个组件能够方便地获取和修改这些状态,并且状态的更改能够被有效地跟踪和响应。这样做的好处包括:
- 提高代码的可维护性:状态的逻辑集中在一处,更容易理解和修改。
- 增强组件的复用性:组件使用状态,不需要关心状态的来源和存储方式
- 实时追踪状态的改变:确保所有组件看到的是相同且最新的状态。
- 处理复杂的状态变化逻辑:例如异步操作、多个状态之间的关联更新等。
常见的 Vue 状态管理库有 Vuex 和 Pinia 等。
在 Vue 中,全局变量可以通过以下几种方式实现并在页面间共享:
- 对于全局变量不需要改变的,可以在 App.vue 中 定义,其他 vue 文件直接使用:https://uniapp.dcloud.net.cn/collocation/App.html#globaldata
// App.vue export default { globalData: { someGlobalVariable: 'This is a global variable' } }; //在其他页面中引入并使用 <template> <view> {{ globalVariable }} </view> </template> <script> import App from '@/App.vue'; export default { computed: { globalVariable() { return App.globalData.someGlobalVariable; } } }; </script>
- 使用 状态管理(Vuex、pinai)
- 创建一个插件文件,例如 globalVariablePlugin.js,在 main.js 中使用插件,然后就可以在组件中使用全局变量
2、状态管理库:Vuex
官网地址:https://vuex.vuejs.org/zh/guide/
vuex 简介
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式, 采用 集中式存储 管理应用的所有组件的状态,解决 多组件数据通信。
- vue 官方搭配,专属使用 (类似于:vue-router),有专门的调试工具
- 集中式管理数据状态方案 ( 操作更简洁 )。( 类似 data() { return { 数据, 状态 }} )
- 数据变化是可预测的 ( 响应式 )
使用 Vuex 的好处:
- 1、数据的存取一步到位,不需要层层传递
- 2、数据的流动非常清晰
- 3、存储在Vuex中的数据都是响应式的
什么样的数据适合存储到 Vuex 中 ?
答案是:需要共享的数据。Vuex 的作用就是:频繁、大范围的数据共享
vue 官方提供的独立于组件体系之外的,管理公共数据的工具
vuex 五个概念
vuex 分为五个大块
- State:存储应用的状态数据。
- Getters:可以对 State 进行计算派生,类似于计算属性。
- Mutations:用于更改 State ,是一个同步方法。
- Actions:用于处理异步操作,并通过提交 Mutations 来更改状态。
- modules: 模块拆分
vuex 的使用
超详细!Vuex手把手教程:https://segmentfault.com/a/1190000040558348
什么是vuex,vuex如何使用:https://blog.csdn.net/m0_70477767/article/details/125155540
使用 Vuex
大致的步骤如下:
- 安装
Vuex
:npm install vuex
。 - 创建一个
store
对象,定义state
、getters
、mutations
和actions
。 - 在
Vue
应用中通过Vue.use(Vuex)
引入Vuex
,并将store
对象传递给new Vue
实例。
在组件中,可以通过 this.$store.state
访问状态,通过 this.$store.getters
访问计算属性,通过 this.$store.commit
提交 mutations
,通过 this.$store.dispatch
分发 actions
来管理应用的状态。
除了 Vuex
,如果是较小的应用,也可以使用简单的全局事件总线或 provide/inject
组合来实现简单的状态共享和管理。
3、状态管理库:PiniaPinia
PiniaPinia 简介
官网:https://pinia.vuejs.org/
新一代状态管理工具,Pinia.js 上手指南:https://segmentfault.com/a/1190000041246156
store (商店、仓库) 可以存储货物,这里是指 组件/页面 之间共享的数据。
Pinia (中文:皮你阿、皮尼亚) 是 Vue 的新一代 store 库 (状态管理库),它提供了一种更简单、更直观的方式来管理应用的状态。通过 Pinia 可以在 组件 / 页面 之间共享一个 state。Pinia 是 vuex 团队成员开发,实现了很多 vuex5 的提案,更加地轻量化且有 devtools 的支持。
与 Vuex 相比,Pinia 提供了一个更简单的 API,没有那么繁琐,提供了组合式 API 风格的 API,最重要的是,当与 TypeScript 一起使用时,它有可靠的类型推理支持。Pinia.js 特点:
- 完整的 typescript 的支持;
- 足够轻量,压缩后的体积只有1.6kb;
- 去除 mutations,只保留 state、getters、anctions 三个概念,减少代码冗余
- actions 支持同步和异步;
- 没有模块module的概念,只有 store 的概念。不用面对一个store下嵌套着许多模块,使用单文件store(有点类似redux/toolkit的一个reducer),可以直接导入其他store使用。store 之间可以自由使用,更好的代码分割;
- 无需手动添加 store,store 一旦创建便会自动添加;
安装:npm install pinia 或者 yarn add pinia 或者 pnpm i pinia
核心 概念
- Store (商店、仓库):https://pinia.vuejs.org/core-concepts/
- State (状态、州、联邦):https://pinia.vuejs.org/core-concepts/state.html
- Getters:https://pinia.vuejs.org/core-concepts/getters.html
- Actions:https://pinia.vuejs.org/core-concepts/actions.html
- Plugins (插件):https://pinia.vuejs.org/core-concepts/plugins.html
- Stores outside of components:https://pinia.vuejs.org/core-concepts/outside-component-usage.html
快速 入门
- Store 是一个包含状态和业务逻辑的实体,它没有绑定到你的 Component 树。换句话说,它托管全局状态。它有点像一个始终存在的组件,每个人都可以读取和写入它。它有三个概念:state、getter 和 actions 。可以假设这些概念等价于组件中的 data、computed 和 methods
创建一个 pinia 实例(根存储)并将其作为 plugin(插件) 传递给 app:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
创建一个 store:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
// could also be defined as
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
})
然后在组件中使用它:
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
counter.count++
// with autocompletion
counter.$patch({ count: counter.count + 1 })
// or using an action instead
counter.increment()
</script>
<template>
<!-- Access the state directly from the store -->
<div>Current Count: {{ counter.count }}</div>
</template>
甚至可以使用一个函数(类似于组件 setup())来定义 Store,用于更高级的用例:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
如果不熟悉 setup() 和 Composition API,Pania 也支持类似 Vuex的 map helpers。可以以相同的方式定义stores,使用 mapStores()、mapState()、mapActions():
const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
const useUserStore = defineStore('user', {
// ...
})
export default defineComponent({
computed: {
// other computed properties
// ...
// gives access to this.counterStore and this.userStore
...mapStores(useCounterStore, useUserStore),
// gives read access to this.count and this.double
...mapState(useCounterStore, ['count', 'double']),
},
methods: {
// gives access to this.increment()
...mapActions(useCounterStore, ['increment']),
},
})
一个更完整的 API 示例
import { defineStore } from 'pinia'
export const useTodos = defineStore('todos', {
state: () => ({
/** @type {{ text: string, id: number, isFinished: boolean }[]} */
todos: [],
/** @type {'all' | 'finished' | 'unfinished'} */
filter: 'all',
// type will be automatically inferred to number
nextId: 0,
}),
getters: {
finishedTodos(state) {
// autocompletion!
return state.todos.filter((todo) => todo.isFinished)
},
unfinishedTodos(state) {
return state.todos.filter((todo) => !todo.isFinished)
},
/**
* @returns {{ text: string, id: number, isFinished: boolean }[]}
*/
filteredTodos(state) {
if (this.filter === 'finished') {
// call other getters with autocompletion
return this.finishedTodos
} else if (this.filter === 'unfinished') {
return this.unfinishedTodos
}
return this.todos
},
},
actions: {
// any amount of arguments, return a promise or not
addTodo(text) {
// you can directly mutate the state
this.todos.push({ text, id: this.nextId++, isFinished: false })
},
},
})
示例:uni-app 使用 pinia
示例:创建一个 "计数器"
创建 Pinia 实例
在 Uni-app 的项目入口文件(通常是 main.js 或 main.ts)中创建 Pinia 实例并将其挂载到应用上。
import { createSSRApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
const pinia = createPinia();
export function createApp() {
const app = createSSRApp(App);
app.use(pinia);
return { app, pinia };
}
定义 Store
Pinia 通过 defineStore 函数来创建一个 store,它有两个参数
- id :store 的 id,必须唯一
- options:一个对象,可以设置 store 的一些选项。也可以使用回调函数返回一个options,回调函数体内的写法类似 vue 的 setup() 写法
在 src 目录下创建一个文件夹用来存放 "你自己的状态管理"。例如:stores/userStore.ts
import {
defineStore
} from 'pinia';
// 定义一个名为 custome_store 的 store
export const custome_store = defineStore('custome_store_id', {
state: () => ({
username: '',
age: 0,
count: 0,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
setUsername(newUsername: string) {
this.username = newUsername;
},
setAge(newAge: number) {
this.age = newAge;
},
increment() {
this.count++
},
doubleCount() {
return count.value * 2
},
},
});
或者使用函数(类似于组件setup())定义 Store:
import {
defineStore
} from 'pinia';
export const custome_store = defineStore('custome_store', () => {
const count = ref(0)
function doubleCount() {
return count.value * 2
}
function increment() {
count.value++
}
return {
count,
increment
}
})
如果不熟悉 setup() Composition API,Pania 也支持类似 Vuex的 map helpers。以相同的方式定义stores,使用mapStores(), mapState(), 或mapActions():
import {
defineStore
} from 'pinia';
const custome_store_1 = defineStore('custome_store_1', {
state: () => ({
count: 0
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
}
}
})
const custome_store_2 = defineStore('custome_store_2', {
// ...
})
export default {
computed: {
// other computed properties
// ...
// gives access to this.counterStore and this.userStore
...mapStores(custome_store_1, custome_store_2)
// gives read access to this.count and this.double
...mapState(custome_store_1, ['count', 'double']),
},
methods: {
// gives access to this.increment()
...mapActions(custome_store_1, ['increment']),
},
}
在组件中使用 Store
在 Uni-app 的组件中,可以通过以下方式使用 Pinia store:
<template>
<view>
<text>Username: {{ userStore.username }}</text>
<text>Age: {{ userStore.age }}</text>
<button @click="updateUserInfo">Update User Info</button>
</view>
<view>
<h2>{{ userStore }}</h2>
<h2>{{ userStore.count }}</h2>
<h2>{{ userStore.doubleCount() }}</h2>
<button @click="userStore.increment">increment</button>
</view>
</template>
<script setup lang="ts">
import { custome_store } from '@/stores/userStore';
const userStore = custome_store();
const updateUserInfo = () => {
userStore.setUsername('New Username');
userStore.setAge(30);
userStore.increment();
};
</script>
在使用Pinia的过程中可以发现自动补全是相当优秀
浏览器 运行
打开开发者工具查看 vue devtool
vue devtool 支持对Pinia状态的增删改查!
Pinia有多种对状态的修改方式
- 使用actions,如上面所示
- 直接在状态上修改:const countPlus_1 = useCounter.count++
- 使用store的$patch函数,支持选项和回调函数两种写法,回调函数适用于状态为数组或其他之类的需要调用状态方法进行修改
const countPlus_2 = useCounter.$patch({ count: useCounter.count + 1 })
const countPlus_3 = useCounter.$patch((state) => state.count++)
对状态的结构需要使用StoreToRefs函数:const { count } = storeToRefs(useCounter)
Pinia的学习和使用是相当友好的,看一遍官方文档就能上手,在上手过程中可以明显地感受到相对于vuex更加快捷,编码体验优秀。