
什么是 Pinia
?
官方描述:Pinia
是一款符合直觉的 Vue.js 状态管理库,它允许你跨组件或页面共享状态。
Pinia
的优势是什么?
-
Devtools 支持
- 追踪状态变化的时间线,并及时给予开发者反馈
- 配合 Vue DevTools, 可以在组件中展示所用到的 Pinia Store
- 让调试更容易的 Time travel
-
HMR 热更新
- 不必重载页面即可修改 Store
- 开发时可保持当前的 State
-
自定义插件:可通过插件扩展 Pinia 功能
-
TypeScript 支持
- 为 JS 开发者提供适当的 TypeScript 支持以及自动补全功能。
-
支持服务端渲染
- Pinia 不仅仅能在 SPA 应用中使用,而且在 SSR 应用 (比如 Nuxt.js)也能够运用
Pinia
和 Vuex
的对比:
Pinia
和 Vuex
都是 Vue.js
的状态管理仓库(容器),但是在设计理念上面,Vuex
和 Pinia
有所不同:
-
是否遵循
Flux
流思想:Vuex
的设计是参照了Flux
流 思想进行设计的 (state 管理状态,actions 派发任务,mutations 更新 state,state 变化重新渲染视图);但是Pinia
取消掉了Flux
流 这一繁琐的操作流程,直接使用单向数据流修改(state 管理状态,actions 更改 state,state 变化重新渲染视图) -
基于第一点,
Pinia
和Vuex
继承 DevTool 的位置不同:Pinia
的 state 变化是在 actions 中监听的,Vuex
的 state 的变化是在 actions 中监听的 -
核心模块的理念不同:
Vuex
的设计是基本遵循单一 store 的思想,而Pinia
允许你的项目存在多个 store,并且由 pinia 统一管理这些 storeVuex
实现多模块的方式是 module 嵌套,而Pinia
实现多模块的方式是多个扁平化的 store 。- 相比之下,
Pinia
更加便捷,因为它无需再考虑是否需要配置 modules 和开启 namespaced
-
配置项不同:
Vuex
的配置更倾向于Options API
风格;而Pinia
的包容性更强,它既可以使用 Options API 定义,也可以使用 Setup 函数 (Composition API)进行定义 -
对于 TypeScript 的支持:Pinia 的支持程度更好,因为它一开始就是用 TypeScript 进行开发的,没有历史包袱。
Pinia 的基本使用:
概述:
使用 Pinia 总共有三步:
- 让 VueApp (Vue 实例) 使用上 pinia 实例
- 定义创建获取 PiniaStore 的 Hook
- 在组件中使用创建 PiniaStore 的 Hook创建一个 Store,直接使用即可
代码演示:
1. 让 VueApp (Vue 实例) 使用上 pinia 实例
js
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
const vueApp = createApp(App);
vueApp.use(createPinia());
vueApp.mount('#app');
2. 定义创建 PiniaStore 的 Hook
- 选项式创建
js
import { defineStore } from 'pinia';
const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount() {
return this.count * 2;
}
},
actions: {
addCount() {
this.count += 1;
}
}
});
export default useCounterStore;
- 组合式创建
js
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
const useTodoListStore = defineStore('todoList', () => {
const todoList = ref([]);
const todoListCount = computed(() => todoList.value.length);
function addTodo(todo) {
if (!todo) {
return;
}
todoList.value = [todo, ...todoList.value];
}
function removeTodo(id) {
todoList.value = todoList.value.filter(item => item.id !== id);
}
function toggleTodo(id) {
todoList.value = todoList.value.map(item => {
if (item.id === id) {
item.completed = !item.completed;
}
return item;
});
}
return {
todoList,
todoListCount,
addTodo,
removeTodo,
toggleTodo,
};
});
export default useTodoListStore;
3. 在组件中使用创建 PiniaStore 的 Hook创建一个 Store,直接使用即可
html
<script setup>
import { ref } from 'vue';
import useCounterStore from '@/store/counterStore';
import useTodoListStore from '@/store/todoListStore';
const counterStore = useCounterStore();
const todoListStore = useTodoListStore();
const addTodoInput = ref('');
function handleAddTodoBtnClick() {
const content = addTodoInput.value.trim();
if (!content) {
return;
}
const todo = {
content,
id: new Date().getTime(),
completed: false
};
todoListStore.addTodo(todo);
}
</script>
<template>
<div class="container">
<div class="counter">
<h1>{{ counterStore.count }}</h1>
<button @click="counterStore.addCount">ADD COUNT</button>
</div>
<hr />
<div class="todo-list">
<div class="add-todo-input">
<input v-model="addTodoInput" placeholder="请输入待办项" />
<button @click="handleAddTodoBtnClick">添加待办项</button>
</div>
<ul class="list">
<li
class="list-item"
v-for="item of todoListStore.todoList"
:key="item.id"
>
<input
type="checkbox"
:checked="item.completed"
@click="todoListStore.toggleTodo(item.id)"
/>
<span :style="{ textDecoration: item.completed ? 'line-through' : '' }">
{{ item.content }}
</span>
<button @click="todoListStore.removeTodo(item.id)">
REMOVE TODO
</button>
</li>
</ul>
</div>
</div>
</template>
像这样,Pinia 的创建和基本使用就完成了!