Pinia学习笔记 | 入门 - 映射辅助函数

Pinia学习笔记

参考文章1:上手 Vue 新的状态管理 Pinia,一篇文章就够了
参考文章2:
作者:南山种子外卖跑手
链接:https://juejin.cn/post/7089032094231298084
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

简介

存在问题:数据不能持久化

Pinia是什么

Pinia是一个全新的Vue状态管理库,是Vuex的代替者(可以理解成Vuex5,Vuex不会再更新),可以实现任意组件之间通信。

Pinia和Vuex

Vuex Pinia
stategettersmutations(同步)、actions(异步)、modules stategettersactions(同步异步都支持)
Vuex4用于Vue3,Vuex3用于Vue2 Vue2和Vue3都支持

Pinia的其他特点

  • 提供扁平结构,每个store都是互相独立的。所以pinia具有更好的代码分割且没有命名空间,也可以通过一个模块中导入另一个模块来隐式嵌套store。
  • Vue2 和 Vue3 都支持,除了初始化安装和SSR配置之外,两者使用上的API都是相同的
  • 支持Vue DevTools
  • 模块热更新
    • 无需重新加载页面就可以修改模块
    • 热更新的时候会保持任何现有状态
  • 支持使用插件扩展 Pinia 功能
  • 支持服务端渲染

mutation 已被弃用,初衷是带来 devtools 的集成方案

代码分割机制案例

某项目有3个store「user、job、pay」,另外有2个路由页面「首页、个人中心页」,首页用到job store,个人中心页用到了user store,分别用Pinia和Vuex对其状态管理。
在这里插入图片描述
先看Vuex的代码分割: 打包时,vuex会把3个store合并打包,当首页用到Vuex时,这个包会引入到首页一起打包,最后输出1个js chunk。这样的问题是,其实首页只需要其中1个store,但其他2个无关的store也被打包进来,造成资源浪费。

在这里插入图片描述

Pinia的代码分割: 打包时,Pinia会检查引用依赖,当首页用到job store,打包只会把用到的store和页面合并输出1个js chunk,其他2个store不耦合在其中。Pinia能做到这点,是因为它的设计就是store分离的,解决了项目的耦合问题。

1.挂载Pinia

安装pinia npm install pinia

Vue3

createPinia:创建大仓库(根容量),大仓库可以管理小仓库

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:安装PiniaVuePlugin插件

import {
    
     createPinia,PiniaVuePlugin } from 'pinia';

Vue.use(PiniaVuePlugin)
const pinia = createPinia() //创建pinia实例
new Vue({
    
    
  router,
  store,
  render: h => h(App),
  pinia
}).$mount('#app')

2.定义store的两种方式options API 和 composition API

defineStore()

  • 第一个参数是表示store的唯一名称id,Pinia 会把所有的模块都挂载到根容器上
  • 第二个参数可接受两类值:Setup 函数或 Option 对象。
    • 第二个参数是Option对象 对应options API的写法
      • state返回初始状态的函数。必须是箭头函数,箭头函数有利于TS类型推导。必须是函数的原因是防止服务端渲染时交叉请求导致数据状态污染(客户端渲染没有任何区别)
      • getters 就是用来封装计算属性,类似于组件的computed,有缓存功能
      • actions就是用来封装业务逻辑,类似与组件的methods,修改 state
    • 第二个参数是Setup函数 对应composition API的写法
      • ref()state 属性,用于存储容器store里的数据
      • computed()getters
      • functionaction,修改 state
  • 返回值是一个函数,该函数调用后返回store容器实例(小仓库)

Pinia会把所有的容器(小仓库)挂在到根容器(大仓库)

使用options API模式定义

// 创建小仓库
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;
    }
  }
});

使用composition API模式

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.业务组件对store的使用

创建store实例

调用 defineStore()返回的函数时创建store实例,store实例是一个被reactive包装的对象

store实例(小仓库)示例,数据被绑定在该实例上
在这里插入图片描述

//组件内使用
<script setup>
//useCounterStore接收defineStore返回的函数
import {
    
     useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>

组件外使用时,必须在函数内部

import {
    
     useAuthUserStore } from '@/stores/auth-user'

router.beforeEach((to, from, next) => {
    
    
	//因为路由器是在其被安装之后开始导航的
  // 必须在函数内部使用,为确保 pinia 实例被激活
  const authUserStore = useCounterStore()
  if (authUserStore.loggedIn) next()
  else next('/login')
})

解构访问Pinia容器数据

直接解构后的count变量会失去响应式,成为一次性数据。

//组件中的代码
<script setup lang="ts">
import {
    
    useMainStore} from '../store'
const {
    
    count} = useMainStore()
</script>

<template>
  <div>{
    
    {
    
    count }}</div>
</template>

解决办法:使用storeToRefs()方法,该方法的作用将解构出来的数据做ref响应式代理

storeToRefs()方法

  • 作用是创建一个引用方法。包含 store 的所有 state、 getter 和 plugin 添加的 state 属性,会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
  • 底层使用toReftoRefs实现的一个 api 方法
<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>

状态更新和Actions

store的$patch():批量更新state
- 参数可以是对象和函数(参数是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>

也可以直接从store中结构action,因为action也被绑定在store上

<script setup lang="ts">
import {
    
    useMainStore} from '../store'
const mainStore = useMainStore()
const {
    
    changeState} = store
</script>

getters使用

//虽然使用了三次,但是只会调用一次,有缓存功能
<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和VueDevtools

在这里插入图片描述

在这里插入图片描述

映射辅助函数

  • mapStores()
  • mapState():将 state 属性映射为 只读的计算属性
  • mapWritableState(): 将 state 属性映射为 可修改的计算属性,类似mapState(),区别是第二个参数不可以传递函数
  • mapActions()

1.不再使用 mapMutations。
2.Pinia为了兼容option api 提供的类似Vuex map 系列的映射辅助函数,不推荐使用。
3. mapGetters = mapState,mapGetters的底层实现逻辑和mapState一样

mapState():将 state 属性映射为 只读的计算属性

  • 使用数组直接 同名映射:…mapState(store, [‘count’])
  • 使用 对象 可映射为 新的名称:…mapState(store, { myOwnName: stateValue| fn (state) })
    • 使用对象时, value 值可以是 字符串,可以是函数;
    • 对象内部也可以直接定义 函数,接收store作为参数

在这里插入图片描述

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
      },
    }),
  },
}

猜你喜欢

转载自blog.csdn.net/qq_41370833/article/details/131702779