跳过正文
  1. 文章/
  2. 前端/
  3. Vue/

12、pinia

·2779 字·6 分钟· loading · loading · ·
前端 Vue
GradyYoung
作者
GradyYoung
Vue - 点击查看当前系列文章
§ 12、pinia 「 当前文章 」

pinia
#

pinia和Vuex的作用是一样的,它也充当的是一个存储数据的作用,存储在pinia的数据允许我们在各个组件中使用。

优点:

  • Vue2和Vue3都支持,这让我们同时使用Vue2和Vue3的小伙伴都能很快上手。
  • pinia中只有state、getter、action,抛弃了Vuex中的Mutation,Vuex中mutation一直都不太受待见,pinia直接抛弃它了,这无疑减少了我们工作量。
  • pinia中action支持同步和异步,Vuex不支持
  • 良好的Typescript支持,毕竟我们Vue3都推荐使用TS来编写,这个时候使用pinia就非常合适了
  • 无需再创建各个模块嵌套了,Vuex中如果数据过多,我们通常分模块来进行管理,稍显麻烦,而pinia中每个store都是独立的,互相不影响。
  • 体积非常小,只有1KB左右。
  • pinia支持插件来扩展自身功能。
  • 支持服务端渲染。

安装pinia
#

npm install pinia

安装完成后我们需要将pinia挂载到Vue应用中,也就是我们需要创建一个根存储传递给应用程序,简单来说就是创建一个存储数据的数据桶,放到应用程序中去。

修改main.ts,引入pinia提供的createPinia方法,创建根存储。

import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();


const app = createApp(App);
app.use(pinia);
app.mount("#app");

创建store
#

store简单来说就是数据仓库的意思,我们数据都放在store里面。当然你也可以把它理解为一个公共组件,只不过该公共组件只存放数据,这些数据我们其它所有的组件都能够访问且可以修改。

在项目src目录下新建store文件夹,用来存放我们创建的各种store,然后在该目录下新建user.ts文件,主要用来存放与user相关的store。

import { defineStore } from 'pinia'


// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users', {
  // 其它配置项
})

创建store很简单,调用pinia中的defineStore函数即可,该函数接收两个参数:

  • name:一个字符串,必传项,该store的唯一id。
  • options:一个对象,store的配置项,比如配置store内的数据,修改数据的方法等等。

我们可以定义任意数量的store,因为我们其实一个store就是一个函数,这也是pinia的好处之一,让我们的代码扁平化了,这和Vue3的实现思想是一样的。

使用store
#

<script setup lang="ts">
  import { useUsersStore } from "../src/store/user";
  const store = useUsersStore();
  console.log(store);
</script>

state
#

state是defineStore参数配置项里面的一个属性。

该属性就是用来存储数据的。

需要注意的是,state接收的是一个箭头函数返回的值,它不能直接接收一个对象。

添加state
#

前面我们利用defineStore函数创建了一个store,该函数第二个参数是一个options配置项,我们需要存放的数据就放在options对象中的state属性内。

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "Tom",
      age: 25,
      sex: "Boy",
    };
  },
});

读取state
#

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>姓名{{ name }}</p>
  <p>年龄{{ age }}</p>
  <p>性别{{ sex }}</p>
</template>
<script setup lang="ts">
  import { ref } from "vue";
  import { useUsersStore } from "../src/store/user";
  const store = useUsersStore();
  const name = ref<string>(store.name);
  const age = ref<number>(store.age);
  const sex = ref<string>(store.sex);
</script>

其实可以用解构的方式来获取值,使得代码更简洁一点。

import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const { name, age, sex } = store;

修改state
#

如果我们想要修改store中的数据,可以直接重新赋值即可,我们在App.vue里面添加一个按钮,点击按钮修改store中的某一个数据。

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>姓名{{ name }}</p>
  <p>年龄{{ age }}</p>
  <p>性别{{ sex }}</p>
  <button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
  import child from './child.vue';
  import { useUsersStore } from "../src/store/user";
  const store = useUsersStore();
  const { name, age, sex } = store;
  const changeName = () => {
    store.name = "张三";
    console.log(store);
  };
</script>

但是需要注意的是:上面这种方法获取的 name 并不是响应式的。

其实,pinia提供了storeToRefs方法给我们,让我们获得的name等属性变为响应式的。

import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);

重置state
#

有时候我们修改了state数据,想要将它还原,我们直接调用store的$reset()方法即可

<button @click="reset">重置store</button>
// 重置store
const reset = () => {
  store.$reset();
};

批量更改state数据
#

如果我们一次性需要修改很多条数据的话,有更加简便的方法,使用store的$patch方法

<button @click="patchStore">批量修改数据</button>
<script setup>
  // 批量修改数据
  const patchStore = () => {
    store.$patch({
      name: "张三",
      age: 100,
      sex: "女",
    });
  };
</script>

直接替换整个state
#

pinia提供了方法让我们直接替换整个state对象,使用store的$state方法。

store.$state = { counter: 666, name: '张三' }

getters
#

getters是defineStore参数配置项里面的另一个属性。getter属性值是一个对象,该对象里面是各种各样的方法。

可以把getter想象成Vue中的计算属性,它的作用就是返回一个新的结果,既然它和Vue中的计算属性类似,那么它肯定也是会被缓存的,就和computed一样。

添加getter
#

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "Tom",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return state.age + 100;
    },
  },
});

使用getter
#

<template>
  <p>新年龄{{ store.getAddAge }}</p>
  <button @click="patchStore">批量修改数据</button>
</template>

<script setup lang="ts">
  import { useUsersStore } from "../src/store/user";
  const store = useUsersStore();
  // 批量修改数据
  const patchStore = () => {
    store.$patch({
      name: "张三",
      age: 100,
      sex: "女",
    });
  };
</script>

getter中调用其它getter
#

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return state.age + 100;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
});

getter传参
#

我们getter函数getAddAge接收了一个参数num,这种写法其实有点闭包的概念在里面了,相当于我们整体返回了一个新的函数,并且将state传入了新的函数。

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "小猪课堂",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
});

actions
#

actions 也是defineStore参数配置项里面的一个属性。

如果我们有业务代码的话,最好就是写在actions属性里面,该属性就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。

添加actions
#

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "Tom",
      age: 25,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return (num: number) => state.age + num;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
  actions: {
    saveName(name: string) {
      this.name = name;
    },
  },
});

使用actions
#

const saveName = () => {
  store.saveName("Lucy");
};
Vue - 点击查看当前系列文章
§ 12、pinia 「 当前文章 」