1. Pinia介绍和Vuex的区别
-
Pinia(发音为/piːnjʌ/,如英语中的“peenya”)是最接近piña(西班牙语中的菠萝)的词;
-
Pinia开始于大概2019年,最初是作为一个实验为Vue重新设计状态管理,让它用起来像组合式API(Composition API)
-
从那时到现在,最初的设计原则依然是相同的,并且目前同时兼容Vue2、Vue3,也并不要求使用Composition API
-
Pinia本质上依然是一个状态管理的库,用于跨组件、页面进行状态共享(这点和Vuex、Redux一样)
-
-
已经有Vuex了吗?为什么还要用Pinia呢?
-
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的仪式,提供了 Composition-API 风格的 API
-
最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持
-
-
Pinia 的优势:
-
mutations 不再存在:
-
经常被认为是 非常 冗长
-
最初带来了 devtools 集成
-
-
更友好的TypeScript支持,Vuex之前对TS的支持很不友好
-
不再有modules的嵌套结构
- 可以灵活使用每一个store,它们是通过扁平化的方式来相互使用的
-
也不再有命名空间的概念,不需要记住它们的复杂关系
-
2. Pinia的安装和基本使用
- 安装(本文使用版本为:2.0.16)
npm install pinia
- createPinia
import { createPinia } from 'pinia' const pinia = createPinia() export default pinia
- store的理解
-
一个 Store (如 Pinia)是一个实体,它会持有为绑定到组件树的状态和业务逻辑,也就是保存了全局的状态
-
它有点像始终存在,并且每个人都可以读取和写入的组件
-
可以在的应用程序中定义任意数量的Store来管理状态
-
- 定义store
-
store 是使用
defineStore()定义的 -
它需要一个唯一名称,作为第一个参数传递
-
这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools
-
返回的函数统一使用useX作为命名方案,这是约定的规范
// 定义关于counter的store import { defineStore } from 'pinia' const useCounter = defineStore('counter', { state: () => ({ count: 99 }) }) export default useCounter -
-
使用
-
store在它被使用之前是不会创建的,可以通过调用use函数来使用store
-
注意store获取到后不能被解构,那么会失去响应式:
- 为了从 Store 中提取属性同时保持其响应式,需要使用storeToRefs()
<template> <div class="home"> <h2>Home Page</h2> <h2>count: {{ counterStore.count }}</h2> <h2>count: {{ count }}</h2> <h2>count: {{ pCount }}</h2> </div> </template> <script setup> import { toRefs } from 'vue' import { storeToRefs } from 'pinia' import useCounter from '../store/counter' const counterStore = useCounter() const { count } = toRefs(counterStore) const { count: pCount } = storeToRefs(counterStore) </script> -
-
Store有三个核心概念:
-
state、getters、actions
-
等同于组件的data、computed、methods
-
一旦 store 被实例化,就可以直接在 store 上访问 state、getters 和 actions 中定义的任何属性
-
3. Pinia核心state
- 使用和修改 store.xxx
- 了解:
- store.$reset
- store.$patch
- store.$state = {}, 赋值一个新的对象
<template> <div class="home"> <h2>Home Page</h2> <h2>name: {{ name }}</h2> <h2>age: {{ age }}</h2> <h2>score: {{ score }}</h2> <button @click="changeUser">修改user</button> <button @click="resetUser">重置user</button> </div> </template> <script setup> import { storeToRefs } from 'pinia' import useUser from '../store/user' const userStore = useUser() const { name, age, score } = storeToRefs(userStore) function changeUser() { // 一个一个修改 // userStore.name = '韩梅梅' // userStore.age = 17 // userStore.score = 100 // 一次性修改多个状态 // userStore.$patch({ // name: 'Jim', // age: 19 // }) // 替换state为新的对象 userStore.$state = { name: '王老师', age: 30, // score: 'max' } } function resetUser() { userStore.$reset() } </script>
4. Pinia核心getters
-
基本使用
-
引入其他的getters
-
getters返回函数
-
getters引入其他store数据
// 定义关于counter的store import { defineStore } from 'pinia' // 引入其他store import useUser from './user' const useCounter = defineStore('counter', { state: () => ({ count: 99, friends: [ { id: 111, name: "kobe", age: 24 }, { id: 112, name: "james", age: 22 }, { id: 113, name: "curry", age: 21 } ], }), getters: { // 基本使用 doubleCount(state) { return state.count * 2 }, // 引入另外的getter doubleCountAddOne() { // this指向store实例 return this.doubleCount + 1 }, // 返回一个函数 getFriendById(state) { return function(id) { return state.friends.find(friend => friend.id === id) } }, // 使用别的store中的数据 showMessage(state) { // 1. 获取user信息 const userStore = useUser() // 2.获取自己的信息 // 3.拼接信息 return `name: ${userStore.name}-count: ${state.count}` } } }) export default useCounter
5. Pinia核心Actions
-
基本使用
-
传入参数
// 定义关于counter的store import { defineStore } from 'pinia' import useUser from './user' const useCounter = defineStore('counter', { state: () => ({ count: 99, friends: [ { id: 111, name: "kobe", age: 24 }, { id: 112, name: "james", age: 22 }, { id: 113, name: "curry", age: 21 } ], }), getters: {...}, actions: { increment(state) { // 此处通过 this 访问实例,不传递state console.log(state) // undefined this.count++ }, incrementWithN(n) { this.count += n } } }) export default useCounter -
发送异步请求
-
返回Promise回调
import { defineStore } from 'pinia' const useHome = defineStore('home', { state: () => ({ banners: [], recommends: [] }), actions: { async fetchHomeMultiData() { const res = await fetch('http://xxx.207.xx.32:8000/home/multidata') const { data } = await res.json() this.banners = data.banner.list this.recommends = data.recommend.list return data } } }) export default useHome<template> <div class="home"> <h2>Home Page</h2> <h2>count: {{ counterStore.count }}</h2> <h2>doubleCount: {{ counterStore.doubleCount }}</h2> <button @click="changeState">count+1</button> <button @click="changeCountWithN">count+10</button> <hr> <h2>banners:</h2> <ul> <template v-for="item in homeStore.banners" :key="item.acm"> <li>{{ item.title }}</li> </template> </ul> </div> </template> <script setup> import useCounter from '../store/counter' import useHome from '../store/home' const counterStore = useCounter() function changeState() { counterStore.increment() } function changeCountWithN() { counterStore.incrementWithN(10) } const homeStore = useHome() homeStore.fetchHomeMultiData().then(res => { console.log('fetchHomeMultiData的action已完成: ', res) }) </script>