Pinia基础入门教程
简介
Pinia 是 Vue.js 的状态管理库,它是 Vuex 的替代方案,在 2021 年左右推出,为 Vue 应用提供了一个简洁、高效的全局状态管理方式。
核心概念
- State(状态)
- 状态是应用中数据的来源。在 Pinia 中,
state
是一个响应式对象。例如,在一个简单的计数器应用中,状态可以存储计数器的当前值。
- 状态是应用中数据的来源。在 Pinia 中,
- Getters(计算属性)
- 类似于 Vue 组件中的计算属性,
getters
可以根据state
的值派生出新的值。 getters
是缓存的,只有当它所依赖的state
发生改变时才会重新计算,这样可以提高性能。
- 类似于 Vue 组件中的计算属性,
- Actions(动作)
actions
是可以改变state
的函数,它们可以是异步的。例如,在计数器应用中,可以定义一个action
来增加计数器的值:
本文使用Vue3
使用Vue3脚手架
使用vite创建一个Vue项目
- 运行Vite创建命令
npm create vite@latest
- 填写项目名称
- 选择创建Vue项目
- 选择项目语言为JavaScript
- 创建成功
-
进入项目根目录,安装项目所需包
npm i
打开项目
-
本人比较喜欢WebStorm,一下操作均使用此IDE进行演示操作,也可使用VSCode等开发工具。
-
以上操作也可以用IDE本身自带的命令窗口进行创建,都没有区别
小技巧
添加快速启动。
点击按钮即可启动
安装Pinia
npm install pinia
具体使用
创建实例
创建一个 pinia 实例 (根 store) 并将其传递给应用:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {createPinia} from "pinia";
const pinia = createPinia()
const app=createApp(App)
app.use(pinia)
app.mount('#app')
定义 Store
-
创建store目录存放store
-
创建JS文件countStore
-
定义一个Store,根据官网提供的规范
-
你可以任意命名
defineStore()
的返回值,但最好使用 store 的名字,同时以use
开头且以Store
结尾。import {defineStore} from 'pinia'; export const useCounterStore = defineStore("counterDate", { state: () => ({ count:0 }), });
-
-
Store
是用defineStore()
定义的,它的第一个参数要求是唯一的 -
defineStore()
的第二个参数可接受两类值:Setup 函数或 Option 对象。本文将采用Option 对象的形式
在App.vue中我们可以暂时使用这里定义的store
<script setup>
import {useCounterStore} from "./store/countStore.js";
const store = useCounterStore()
const count = store.count
</script>
<template>
<div>
<p>count的值</p> {{count}}
</div>
</template>
<style scoped>
</style>
页面中我们可以看到count的值被正确的渲染出来
通过vue的浏览器工具,可以看到pinia的值并可以修改
Getter
Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore()
中的 getters
属性来定义它们。推荐使用箭头函数,并且它将接收 state
作为第一个参数
-
定义Getters
import {defineStore} from 'pinia'; export const useCounterStore = defineStore("counterDate", { state: () => ({ count:0 }), getters:{ doubleCount: (state) => state.count * 2 } });
大多数时候,getter 仅依赖 state。不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过
this
访问到整个 store 实例,但(在 TypeScript 中)必须定义返回类型。这是为了避免 TypeScript 的已知缺陷,不过这不影响用箭头函数定义的 getter,也不会影响不使用this
的 getter。 -
在App.vue中我们可以简单使用一下Getters
<script setup> import {useCounterStore} from "./store/countStore.js"; const store = useCounterStore() const increment=()=>{ store.count++ } </script> <template> <div> <button @click="increment">加一</button> <p>count的值</p> {{store.count}} <p>调用getter</p>{{store.doubleCount}} </div> </template> <style scoped> </style>
通过点击我们可以看到count的值每次都加一,但是store.doubleCount
的值每次都是count的二倍。
Actions
在上面的小例子中我们使用increment
方法对count进行更改,但是如果多个页面需要对count进行更改,这样就需要定义多个方法,整体代码就不够优雅简洁。接下来我们定义一个actions
来对state进行修改。
-
定义actions
import {defineStore} from 'pinia'; export const useCounterStore = defineStore("counterDate", { state: () => ({ count:0 }), getters:{ doubleCount: (state) => state.count * 2 }, actions:{ increment() { this.count++; } } });
-
在App.vue中进行简单使用
<script setup> import {useCounterStore} from "./store/countStore.js"; const store = useCounterStore() </script> <template> <div> <button @click="store.increment()">加一</button> <p>count的值</p> {{store.count}} <p>调用getter</p>{{store.doubleCount}} </div> </template> <style scoped> </style>
可以看到,和上面的效果是一样的。
不同页面中使用
通过上述的简单了解以及使用,可以发现,我们通过定义变量的方式也可以实现上述功能,就显得使用pinia有些许赘余,但是在父子组件,兄弟组件,甚至没有关系的组件中,我们都可以通过Store进行联系,接下来我将演示在兄弟组件中,使用pinia的方便快捷。
-
创建Home.vue文件
<script setup> import {useLoginStore} from "../store/loginStore.js"; import {useUserStore} from "../store/userStore.js"; const store=useLoginStore() const userStore=useUserStore() </script> <template> <div> <span v-if="!store.isLogin">账号:{{userStore.userData.username}}</span> <br/> <button v-if="!store.isLogin" @click="store.login">登录</button> </div> </template> <style scoped> </style>
-
创建Login.vue文件
<script setup> import {useLoginStore} from "../store/loginStore.js"; import {ref} from "vue"; const store = useLoginStore() const loginForm = ref({ username:'', password:'' }) </script> <template> <div class="login-container"> <h2>登录</h2> <form> <input type="text" v-model="loginForm.username" name ="username" placeholder="请输入用户名" required> <input type="password" v-model="loginForm.password" name="password" placeholder="请输入密码" required> <button @click="store.login(loginForm)">登录</button> </form> </div> </template> <style scoped> .login-container{ border: 1px solid green; } </style>
-
创建loginStore.js
import {defineStore} from 'pinia'; import { useUserStore } from './userStore'; export const useLoginStore = defineStore("loginStore", { state: () => ({ isLogin:false }), actions:{ login(data) { this.isLogin=!this.isLogin const userStore = useUserStore() userStore.setData(data); } } });
-
创建userStore.js
import {defineStore} from 'pinia'; export const useUserStore = defineStore("userStore", { state: () => ({ userData:{} }), actions:{ setData(data) { this.userData=data } } });
-
编写App.vue
<script setup> import Login from "./components/Login.vue"; import Home from "./components/Home.vue"; import {useLoginStore} from "./store/loginStore.js"; const store = useLoginStore() </script> <template> <div> <Home/> <Login v-if="store.isLogin"/> </div> </template> <style scoped> </style>
这段代码简单描述了兄弟组件相互调用以及数据共享的相关方法,相对与通过中间组件完成兄弟组件的通信更加简单。
结语
Pinia还有很多使用方法,例如订阅 、持久化插件等等。详情可查看Pinia官网。