Article Directory
Pinia Study Notes
Reference article 1: Get started with Vue’s new state management Pinia, one article is enough
Reference article 2:
Author: Nanshan Seed Delivery Runner
Link: https://juejin.cn/post/7089032094231298084
Source: Rare Earth Nuggets
Copyright belongs to the author all. For commercial reprint, please contact the author for authorization, for non-commercial reprint, please indicate the source.
Introduction
There is a problem: the data cannot be persisted
What is Pinia?
Pinia is a brand-new Vue state management library, a replacement for Vuex (it can be understood as Vuex5, Vuex will not be updated), and can realize communication between any components.
Pinia and Vuex
Vuex | Pineapple |
---|---|
state , getters , mutations (synchronous), actions (asynchronous),modules |
state , getters , actions (both synchronous and asynchronous are supported) |
Vuex4 for Vue3, Vuex3 for Vue2 | Both Vue2 and Vue3 support |
Other features of Pinia
- Provide a flat structure, each store is independent of each other. So pinia has better code segmentation and no namespace, and you can also implicitly nest stores by importing another module in one module.
- Both Vue2 and Vue3 support, except for initial installation and SSR configuration, the APIs used by both are the same
- support
Vue DevTools
- Module hot update
- Modules can be modified without reloading the page
- Any existing state will be maintained during a hot update
- Support for extending Pinia functionality with plugins
- Support server-side rendering
mutation has been deprecated, the original intention is to bring devtools integration solution
Code Splitting Mechanism Case
A certain project has 3 stores "user, job, pay", and 2 routing pages "home page, personal center page". The home page uses the job store, and the personal center page uses the user store. Pinia and Vuex are used to check the status respectively. manage.
First look at the code segmentation of Vuex: When packaging, vuex will merge and package the three stores. When Vuex is used on the homepage, this package will be introduced to the homepage and packaged together, and finally a js chunk will be output. The problem with this is that, in fact, only one store is needed for the home page, but the other two irrelevant stores are also packaged in, resulting in a waste of resources.
Pinia's code segmentation: When packaging, Pinia will check the reference dependencies. When the home page uses the job store, the packaging will only combine the used store and the page to output a js chunk, and the other two stores will not be coupled in it. Pinia can do this because its design is that the store is separated, which solves the coupling problem of the project.
1. Mount Pinia
install pinianpm install pinia
View 3
createPinia
: Create a large warehouse (root capacity), and the large warehouse can manage the small warehouse
import {
createApp } from 'vue'
import {
createPinia } from 'pinia'; //引入createPinia
import App from'./App.vue' //引入根组件
const pinia = createPinia() //创建pinia实例 大仓库
const app = creatApp(App) //创建Vue应用实例
app.use(pinia)//安装pinia插件
app.mount('#app')
Vue2: install PiniaVuePlugin
plugin
import {
createPinia,PiniaVuePlugin } from 'pinia';
Vue.use(PiniaVuePlugin)
const pinia = createPinia() //创建pinia实例
new Vue({
router,
store,
render: h => h(App),
pinia
}).$mount('#app')
2. Two ways to define store options API and composition API
defineStore()
:
- The first parameter is the unique name id representing the store, and Pinia will mount all modules on the root container
- The second parameter accepts two types of values: a Setup function or an Option object.
- The second parameter is
Option对象
the writing method corresponding to the options APIstate
A function that returns the initial state. Must be an arrow function, which is good for TS type deduction. The reason why it must be a function is to prevent data state pollution caused by cross-requests during server-side rendering (client-side rendering makes no difference)getters
It is used to encapsulate computed properties, similar to computed of components, with caching functionactions
It is used to encapsulate business logic, similar to methods of components, to modify state
- The second parameter is
Setup函数
the writing method corresponding to the composition APIref()
Yesstate
attribute, used to storestore
the data in the containercomputed()
yesgetters
function
Yesaction
, modify state
- The second parameter is
store
The return value is a function that returns the container instance (small warehouse) after the function is called
Pinia will hang all containers (small warehouses) on the root container (big warehouse)
Defined using the options API schema
// 创建小仓库
import {
defineStore } from 'pinia';
export const useCounterStore = defineStore('counterForOptions', {
state: () => {
return {
count: 1 };
},
actions:{
changeState(){
//通过this访问容器里的数据
this.count++
}
}
getters: {
//参数state是状态数据,可选参数
doubleCount(state) {
return state.count * 2;
}
doubleCount1(state):number {
//也可以使用this,但是类型推导存在问题,必须手动指定返回值类型
return this.count * 2;
}
}
});
Using the composition API pattern
import {
ref, computed } from 'vue';
import {
defineStore } from 'pinia';
export const useCounterStore = defineStore('counterForSetup', () => {
const count = ref<number>(1);
const doubleCount = computed(() => count.value * 2);
function increment() {
count.value++;
}
return {
count, doubleCount, increment };
});
2. The use of store by business components
Create a store instance
Create an instance when calling defineStore()
the returned function store
, store
the instance is a wrapped reactive
object
store
Instance (small warehouse) example, data is bound to the instance
//组件内使用
<script setup>
//useCounterStore接收defineStore返回的函数
import {
useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>
When used outside the component, it must be inside the function
import {
useAuthUserStore } from '@/stores/auth-user'
router.beforeEach((to, from, next) => {
//因为路由器是在其被安装之后开始导航的
// 必须在函数内部使用,为确保 pinia 实例被激活
const authUserStore = useCounterStore()
if (authUserStore.loggedIn) next()
else next('/login')
})
Destructuring access to Pinia container data
The directly destructured count variable will lose its responsiveness and become one-time data.
//组件中的代码
<script setup lang="ts">
import {
useMainStore} from '../store'
const {
count} = useMainStore()
</script>
<template>
<div>{
{
count }}</div>
</template>
Solution: use storeToRefs()
the method, the role of this method is to use the deconstructed data as ref
a responsive proxy
storeToRefs()
method
- The role is to create a reference method. Contains all state, getter and plugin added state attributes of the store, and skips all action or non-responsive (not ref or reactive) attributes
toRef
An api method used andtoRefs
implemented by the bottom layer
<script setup lang="ts">
import {
useMainStore} from '../store'
import {
storeToRefs} from 'pinia'
const {
count} = storeToRefs(useMainStore())
/*ObjectRefImpl
{
"_object": {
"count": 1
},
"_key": "count",
"__v_isRef": true
}
*/
console.log(count)
console.log(count.value) //1
</script>
Status Updates and Actions
store $patch()
: batch update state
- parameters can be objects and functions (parameters are state)
<script setup lang="ts">
import {
useMainStore} from '../store'
import {
storeToRefs} from 'pinia'
const mainStore = useMainStore()
const {
count} = storeToRefs(useMainStore())
const changeCount = ()=>{
//方式1:最简单的方式
// mainStore.count++;
//方式2:如果需要多个数据,建议使用$patch,批量更新
//mainStore.$patch({
// count:mainStore.count+1,
//...数据名:修改后的值
//涉及数组很麻烦
// })
//方式3:$patch(函数)其中函数的参数是state就是store的state,批量更新
//mainStore.$patch(state=>{
// state.count++
//})
//方法4:逻辑比较多的时候可以封装到actions做处理,
mainStore.changeState()
}
</script>
It can also be constructed directly store
from it action
, because the action is also bound to the store
<script setup lang="ts">
import {
useMainStore} from '../store'
const mainStore = useMainStore()
const {
changeState} = store
</script>
getters use
//虽然使用了三次,但是只会调用一次,有缓存功能
<template>
<div>
<div>{
{
mainStore.count }}</div>
<p>
<button @click="changeCount">修改数据</button>
</p>
<p>{
{
mainStore.doubleCount}}</p>
<p>{
{
mainStore.doubleCount}}</p>
<p>{
{
mainStore.doubleCount}}</p>
</div>
</template>
Pinia and VueDevtools
mapping helper function
mapStores()
mapState()
:state
Maps the property as a read-only computed propertymapWritableState()
: Mapstate
the attribute to a modifiable computed attribute, similarmapState()
, the difference is that the second parameter cannot pass a functionmapActions()
1. No longer use mapMutations.
2. In order to be compatible with the mapping helper functions similar to the Vuex map series provided by the option api, Pinia is not recommended.
3. mapGetters = mapState, the underlying implementation logic of mapGetters is the same as mapState
mapState() : state
Map properties as read-only computed properties
- Use arrays to map directly with the same name:
…mapState(store, [‘count’])
- Use objects mappable to new names:
…mapState(store, { myOwnName: stateValue| fn (state) })
- When using an object,
value
the value can be a string or a function; - Functions can also be defined directly inside the object, receiving
store
as parameters
- When using an object,
import {
mapState} from 'pinia'
import {
useCounterStore } from '../stores/counter'
export default {
computed: {
// 生成的计算属性名字为count,值等于 store.count
...mapState(useCounterStore, ['count'])
// 第二个参数是对象的写法
...mapState(useCounterStore, {
myOwnName: 'count',
// 你也可以写一个函数来获得对 store 的访问权
double: store => store.count * 2,
// 它可以访问 `this`,但它没有标注类型...
magicValue(store) {
return store.someGetter + this.count + this.double
},
}),
},
}