今天想吃菠萝了--Pinia快速消化

前言

作为vue的公共状态管理库,已经有了青梅竹马vuex,为什么还会再出现一个天降之物pinia呢?虽然现在vuex还没有被淘汰,vue现在左拥右抱两大公共状态管理库,但青梅竹马总是敌不过天降之物呀

pinia对比vuex有什么优势呢?为什么要选择pinia

以下是官方给出的几大优势:

除了这几点对比vuex还有就是体积小,pinia体积只有1kb左右,娇小可爱谁不喜欢呢?其次易学好上手,简单的api大大降低了上手难度!

学习pinia

首先安装pinia

js 复制代码
yarn add pinia
// 或者使用 npm
npm install pinia

定义pinia

js 复制代码
//main.js

//只需要简单的注册以下即可
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
createApp(App).use(createPinia()).mount("#app");

//store.js

//在这里只需要做一件事就可以
//从pinia中引入defineStore  然后再将defineStore的实例导出去即可
import { defineStore } from "pinia";
//defineStore有三个参数
//第一个参数是公共状态的id,也是唯一的
//第二个参数有两种,一种为对象,Options API形式,另一种为回调函,Composition API形式
//第三个参数是在pinia引入插件的时候所使用的,一般情况用不到
export const useStore = defineStore("store", {
//...
});

Options API写法

使用pinia定义store

ts 复制代码
//store.ts

import { defineStore } from "pinia";
export const useStore = defineStore("store", {
  //这里存放的就是公共状态,在pinia里不用害怕使用箭头函数会找不到store的实例
  state: () => ({
    name: "张三",
  }),
  //这里为计算值,接收一个state作为参数,他是惰性的,只有状态改变时他才会运行
  //请注意,在执行此操作时,getter 不再缓存,它们只是您调用的函数。 
  //但是,您可以在 getter 本身内部缓存一些结果,这并不常见,但应该证明性能更高
  getters: {
    updataName: (state) => state.name + "是个人",
    //访问内部状态,也可以使用this获取到,即使是使用的箭头函数
    isHeight() {
      return this.updataName + "ta的身高是一米八";
    },
  },
  //在这里定义一些方法来改变sotre内部的状态
  actions: {
    //方法所接收的参数为传进来的参数
    setName(name: string) {
      this.name = name;
    },
    // 异步操作
    //因为没有mutation,这里既可以执行同步任务,也可以执行异步任务
    //使我们写公共状态的心理负担又下降了许多
    async awaitSetData(value: string) {
      setTimeout(() => {
        this.name = value;
      }, 2000);
    },
  },
});

Options API写法的组件中调用

js 复制代码
//app.vue

<template>
  <div>
    <button @click="setName('李四')">改变名字</button>
    <button @click="set">改变名字</button>
    <button @click="awaitSetData('李四')">异步改变名字</button>
    <!-- 因为store是被useStore()函数返回的 因此可以像这样直接使用store.updataName -->
    <h1>{{ updataName }}</h1>
    <h1>{{ store.updataName }}</h1>
    <!-- 像这两种写法,得到的结果也是一致的 -->
    <h1>{{ name }}</h1>
    <h1>{{ store.name }}</h1>
  </div>
</template>

<script lang="ts">
//从pinia文件中引入 导出的实例方法
import { useStore } from "./store/home";
//这里pinia提供了与vuex的相似的api 这些辅助函数的使用方法也是与vuex类似的
import { mapState, mapActions } from "pinia";
export default {
  data() {
    return {
      //这里实例方法所得到的就是那个文件定义的store
      //这里可以理解为vuex中的store
      store: useStore(),
    };
  },
  computed: {
    //辅助函数也是同vuex一样有着两种写法,看个人喜好使用,gatter也是使用了mapState
    //但与之不同的是他有两个参数,第一个是导出store实例方法,第二个才是状态
    ...mapState(useStore, ["updataName", "name"]),
    ...mapState(useStore, {
      n: "name",
      upN: "updataName",
    }),
  },
  methods: {
    //方法定义,一样的似曾相识,有两种,选择其中一种使用即可,也是两个参数,第一个是导入的实例方法
    ...mapActions(useStore, ["setName", "awaitSetData"]),
    ...mapActions(useStore, {
      sName: "setName",
      awaitName: "awaitSetData",
    }),
    set() {
      //这里的store是data中的store,但是被赋值useState的实例,所以有着store的方法
      this.store.setName("李四");
    },
  },
};
</script>

这样使用Options API写法,pinia的定义和使用就完成了

Composition API

使用Composition API定义,比Options API写法简单许多

ts 复制代码
//store.ts

import { defineStore } from "pinia";
import { ref, reactive, computed } from "vue";
export const useStore = defineStore("store", () => {
  //只需要使用vue的ref或reactive定义响应数,再retrun出去,就算定义好状态了
  //可以使用类似于Options API写法一样,将所有的状态存放到一个对象中,调用的时候直接.属性值就可以了
  const state = reactive({
    userInfo: {},
  });
  //单独定义一个状态
  const name = ref("张三");
  //getter是使用computed计算属性来实现了,计算属性还会缓存,比getter又多了点东西
  const updateName = computed(() => {
    return name.value + "是个人";
  });
  //当然也可以使用可传参的计算属性
  const updateNameTow = computed(() => (v: string) => {
    return name.value + v;
  });
  // 定义方法直接写fn函数就可以了
  const setName = (v: string) => {
    name.value = v;
  };
  //这里也可以是异步的
  const setNameAsync = async (v: string) => {
    await new Promise((resolve) => {
      setTimeout(() => {
        name.value = v;
        resolve(v);
      }, 1000);
    });
  };
  //唯一不好的就是所有的状态或方法都需要return出去,内部就相当于一个工厂函数,处理完之后将处理好的数据返回出去
  return {
    state,
    name,
    updateName,
    updateNameTow,
    setName,
    setNameAsync,
  };
});

这样看是不是比Options API写法更清晰一些,写的状态也没必要再上下翻着看了
Composition API方法使用也是比较简单的

js 复制代码
//app.vue

<template>
  <div>
    <!-- 一切都于预想的一样正常 -->
    <button @click="set">改变名字</button>
    <h1>{{ store.name }}</h1>
    <!-- 解构后的方法使用,是不是Options API写法中使用了跟辅助函数类似呢 -->
    <button @click="setName('李四')">改变名字</button>
    <!-- 解构出来的值也是响应的,正常使用 -->
    <h1>{{ name }}</h1>
    <h2>{{ newName }}</h2>
    <!-- 这里渲染出来就是 store.name + 一米八 -->
    <h1>{{ updateNameTow("一米八") }}</h1>
    <h1>{{ updateName }}</h1>
  </div>
</template>

<script setup lang="ts">
//引入之后直接定义赋值给一个实例,就可以正常使用了
import { useStore } from "../store/home";
//定义store实例
const store = useStore();
const set = () => {
  //实例中的方法随便调用
  store.setName("李四");
};
/* 在这里方法是可以使用es6解构赋值,直接解构的,解构后一样的可以正常执行 */
const { setName } = store;
//或者  省了一步定义实例的操作
//const { setName } = useStore()

//既然方法可解构,状态是不是也能解构
//可以但不能直接解构,否者会失去响应性,pinia中提供了一个storeToRefs来解决了这个问题
//这样解构出来的状态就是响应式的
const { name } = storeToRefs(store); //也可以是storeToRefs(useStore())
//不仅可以使用pinia提供的方法 ,也可以使用vue提供的toRefs方法
//解构赋值的重命名应该都知道吧
const { name: newName } = toRefs(store); //也可以是toRefs(useStore())
//计算属性也是一样的需要使用方法来进行解构,但是可传值的计算数据类似于方法,所以可以直接解构
//这里updateNameTow是响应的,updateName却不是
const { updateNameTow, updateName } = store;
</script>

看着是不是很简单,写的情况比较多,所以显得多一些,实际上使用只需要一到两行代码就可以使用pinia的store了
上面看我在使用vue的api和pinia的api的时候是没有引入的,是因为我用了插件,正常写的时候别忘了引入api ,也可以使用插件,看我另一篇:在vite中,如何让vue项目优雅的自动引入组件 - 掘金 (juejin.cn)

pinia实例上的一些方法

当我们打印了pinia的store实例之后会发现,会有一些它内部自带的方法

这些方法的作用都是哪些

$dispose

$dispose 是一个用于清理 store 的方法。当您不再需要某个 store 或者在组件卸载时,可以调用 $dispose 方法来释放该 store 所占用的资源,以避免内存泄漏。

js 复制代码
  const store = useStore();  
// 在组件卸载时清理 store  
    beforeUnmount(() => {  
      store.$dispose();  
    });  

$patch

$patch 是一个用于更新 store 状态的方法。它允许您以更细粒度的方式更新状态,而不需要完全替换整个状态对象。

$patch 方法接受一个对象作为参数,该对象包含要更新的状态属性和对应的值。只有指定的属性会被更新,其他状态属性保持不变。

js 复制代码
//store.ts

//需要传入一个对象,对应相应的变量名即可
export const useStore = defineStore("store", () => {
  const state = reactive({
    userInfo: {},
  });
  const name = ref("张三");
  return {
    state,
    name,
  };

//app.vue

const store = useStore();
//这两种都是可以的 执行后会立马改变
store.$patch({ name: "李四" });
store.$patch({ state: { userInfo: { name: "李四" } } });

$reset

这个方法很简单,$reset 是一个用于重置 store 的方法。它将 store 的状态、getter、action 和订阅全部重置为初始状态。

js 复制代码
const store = useStore();  
//改变了公共状态的值
//...
//调用$reset方法,之后会全部重置如初
store.$reset()

$subscribe

$subscribe 是一个用于订阅 store 变化的方法。它允许您监听 store 的状态变化,并在状态发生变化时执行相应的逻辑。

$subscribe 方法接受一个回调函数作为参数,该回调函数将在 store 的状态发生变化时被触发。回调函数接收两个参数:新的状态对象和一个包含变化详情的对象。

js 复制代码
//知道状态什么时候改变了,改变的同时做一些操作
const store = useStore(); 
//...
//状态改变时会调用这个方法
store.$subscribe((n, s) => {
  console.log(n, s);
});

$onActions

这个时用于订阅actions的一个方法,接收两个参数,第一个是回调函数,执行一些操作,第二个为布尔值,当为ture的时候,当组件卸载时不会自动卸载当前订阅

js 复制代码
const store = useStore()
const unsubscribe = store.$onAction(
  ({
    name, // 方法的名字
    store, // store 实例
    args, // 调用这个 action 的参数
    after, // 在这个 action 执行完毕之后,执行这个函数
    onError, // 在这个 action 抛出异常的时候,执行这个函数
  }) => {
    //方法改变时做一些操作,异步的时候是一个不错的选择
    //...
    after(() => {
      //方法执行完毕后做一些操作
    });
    onError(() => {
      //方法执行失败后做一些操作
    });
  }
);

// 手动移除订阅
unsubscribe();

pinia插件

这里pinia插件使用方法与vue插件使用方法类似,就不过多说明了,感兴趣的可以搜索相关资料

js 复制代码
import { createPinia } from 'pinia'

// 为安装此插件后创建的每个store添加一个名为 `secret` 的属性
// 这可能在不同的文件中
function SecretPiniaPlugin() {
  return { secret: 'the cake is a lie' }
}

const pinia = createPinia()
// 将插件提供给 pinia
pinia.use(SecretPiniaPlugin)

// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'

服务端渲染

前面也说过,pinia的优势也在于服务端渲染,只要在 setup 函数、gettersactions 的顶部调用 useStore() 函数,使用 Pinia 创建store应该可以立即用于 SSR,也就是vue3中正常写就应用于SSR了,在setup之外或者js文件中使用

js 复制代码
const pinia = createPinia()
const app = createApp(App)

app.use(router)
app.use(pinia)

router.beforeEach((to) => {
  // 当前运行的应用
  const main = useMainStore(pinia)

  if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'
})

结尾

pinia同vuex一样,是单向数据流的,在vue文件内是不能更改状态的,另外介绍这么久pinia没有发现有module这一块的内容,是因为pinia可以创建多个文件,多个store,每一个文件就是一个模块,使用时如果需要多个模块的store,直接分分文件写就行了,相互之间也并不冲突!

pinia作为vue天降之物的公共状态管理库,确实有着很多突出的点,学习起来也比较简单,如果是你,是选择pinia还是vuex?

相关推荐
come1123412 分钟前
Vue 响应式数据传递:ref、reactive 与 Provide/Inject 完全指南
前端·javascript·vue.js
前端风云志34 分钟前
TypeScript结构化类型初探
javascript
musk12121 小时前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘1 小时前
js代码09
开发语言·javascript·ecmascript
万少2 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL2 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl022 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js