今天想吃菠萝了--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?

相关推荐
Json____8 分钟前
学法减分交管12123模拟练习小程序源码前端和后端和搭建教程
前端·后端·学习·小程序·uni-app·学法减分·驾考题库
迂 幵17 分钟前
vue el-table 超出隐藏移入弹窗显示
javascript·vue.js·elementui
上趣工作室21 分钟前
vue2在el-dialog打开的时候使该el-dialog中的某个输入框获得焦点方法总结
前端·javascript·vue.js
家里有只小肥猫21 分钟前
el-tree 父节点隐藏
前端·javascript·vue.js
fkalis22 分钟前
【海外SRC漏洞挖掘】谷歌语法发现XSS+Waf Bypass
前端·xss
zxg_神说要有光1 小时前
自由职业第二年,我忘记了为什么出发
前端·javascript·程序员
陈随易1 小时前
农村程序员-关于小孩教育的思考
前端·后端·程序员
云深时现月1 小时前
jenkins使用cli发行uni-app到h5
前端·uni-app·jenkins
昨天今天明天好多天1 小时前
【Node.js]
前端·node.js
亿牛云爬虫专家2 小时前
Puppeteer教程:使用CSS选择器点击和爬取动态数据
javascript·css·爬虫·爬虫代理·puppeteer·代理ip