手撕pinia源码(一): pinia的使用

168 阅读2分钟

本章将从0到1实现pinia仓库,除了实现其基本功能外,并且还会实现$dispose、$onAction、$path、$reset、$subscribeAPI,坚持看下去,其实pinia真的很简单!!!

手撕pinia源码目录

手撕pinia源码(一): pinia的使用

手撕pinia源码(二):实现defineStore

手撕pinia源码(三):实现pinia内置方法

源码地址传送门

简单介绍

  • pinia是用来取代vuex的,pinia非常小巧,即支持vue2也支持vue3,同时typeScript类型支持也非强友好
  • pinpa默认支持多仓库,在vuex中只有一个仓库,会导致所有的状态都放在同一个store里,当我们使用模块化时,也会让代码显得非常复杂,例如vuex.$store.a.b.c……,假如模块化分得细的话,就会非常难以维护。而在pinia中,选择将所有的状态都模块化,根据id存放在store里,需要用时只需要调用APIuseXXX就可以很方便的取到对应的仓库。
  • vuex中,会有state、getter、mutation、action、module这些API,其中mutation是更改状态的提交方法,action是提交mutation的方法,其可以包含异步操作(这里有一个很大的误区,actions并不是用于异步的,而是它可以包含异步操作,我们在写同步方法也是完全没问题的),关于mutationaction到底什么时候用哪个?就成了一个很头疼的问题,其实actions最核心是封装,封装各种提交mutation的方法一次调用。
  • 为了解决vuexmutationaction的痛点,pinia直接舍弃了matution层,只有action层,所以用户所有更改只需要使用action即可。

安装

根据pinia官网指引安装即可。

基本使用

defineStore的写法

  • 第一种optionsAPI
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counterStore', {
  state: () => {
    return {
      counter: 0
    }
  },
  actions: {
    increment() {
      this.counter++
    }    
  },
  getters: {
    dobuleCount() {
      return this.counter * 2
    }
  }
})
  • 代码3行,defineStore创建定义一个仓库,第一个参数是唯一值id,第二个参数是对象,也就是optionsAPi
  • 代码4行,选项式的state必须是一个箭头函数
  • 代码9行,选项式actions是一个对象
  • 代码14行,选项式getters是一个对象,其属性值为函数,必须返回一个值,跟vuecomputed用法差不多。

第二种compositionAPI

import { ref, computed } from 'vue'
import { defineStore } from '@/pinia'

export const useCounterStore2 = defineStore('counterStore2', () => {
  const counter = ref(0);
  function increment() {
    this.counter++
  }
  const dobuleCount = computed(() => {
    return counter.value * 2;
  })
  return { counter, increment, dobuleCount }
})
  • 代码4行,defineStore依旧第一个参数是唯一值id,第二个参数是一个函数
  • 代码5行,在函数里面,写法跟vuecompositionAPI一致
  • 代码12行,把所有的属性和方法返回出去即可。

第三种optionsAPI

import { defineStore } from 'pinia'

export const useCounterStore = defineStore({
  id: 'counterStore',
  state: () => {
    return {
      counter: 0
    }
  },
  actions: {
    increment() {
      this.counter++
    }    
  },
  getters: {
    dobuleCount() {
      return this.counter * 2
    }
  }
})

以上两种写法其实现的效果完全一样,请记住这两种写法,后续实现时,会详细讲解这两种写法的区别和实现。

API的基本用法

$dispose

我们可以让某个时store停止响应,延用上面的例子,useCounterStore将不再响应。

<script setup>
import { useCounterStore } from "./stores/counter";
const store = useCounterStore();
store.$dispost();
</script>

$patch

补丁操作,可以同时批量设置store的值,传入一个回调函数,回调函数接收一个store参数可访问当前仓库的所有属性方法。

<script setup>
import { useCounterStore } from "./stores/counter";
const store = useCounterStore();
store.$patch((store) => {
    store.counter = 1000;
});
</script>

$reset

重置仓库数据,此API只能用于通过options方式构建的store,后面会讲解为何只有options方式构建的store才支持此API。

<script setup>
import { useCounterStore } from "./stores/counter";
const store = useCounterStore();
store.$reset()
</script>

$subscribe

设置一个回调函数,仓库所有状态被修改时执行此回调函数,返回一个删除此回调的函数。

<script setup>
import { useCounterStore } from "./stores/counter";
const store = useCounterStore();
store.$subscribe((params, state) => {
    console.log("params",params);//修改的属性
    console.log("state",state);//修改后的数据
})
</script>

$onAction

设置一个回调函数,每次调用操作时执行此回调函数,并且回调函数接收一个参数,参数包含所有操作信息,

<script setup>
import { useCounterStore } from "./stores/counter";
const store = useCounterStore();
store.$onAction(({name, store, args, after, onError}) => {
    after((result) => {
        // 修改后的结果
    })
    
    onError((error) => {
        // 执行错误
    })
})
</script>

介绍完基本的概念之后,进入下一章将开始一步步实现所有的功能。