Pinia快速入门(这些完全够用)

1. pinia是什么?

官网解释:

Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。

pinia是针对vue3推出的状态管理工具,状态管理,简单来说就是一个存储数据的地方,存放在pinia中的数据在各个组件中都能访问到,它是Vue生态中重要的组成部分。

2. 为什么要使用pinia?

vue2时代,状态管理工具是Vuex,实际上,pinia就是Vuex的升级版,官网也说过,为了尊重原作者,所以取名pinia,而没有取名Vuex,所以大家可以直接将pinia比作为Vue3的Vuex。

pinia的优点:

  • Vue2和Vue3都支持。
  • pinia中只有state、getter、action,抛弃了Vuex中的Mutation
  • pinia中action支持同步和异步,Vuex不支持
  • 良好的Typescript支持
  • 无需再创建各个模块嵌套了,pinia中每个store都是独立的,互相不影响。
  • 体积非常小,只有1KB左右。
  • pinia支持插件来扩展自身功能。
  • 支持服务端渲染。

pinia的优点还有非常多,上面列出的主要是它的一些主要优点,更多细节的地方还需要大家在使用的时候慢慢体会。

3. pinia基础使用

3.1. 安装pinia

和vue-router、vuex等一样,我们想要使用pinia都需要先安装它,安装它也比较简单。

安装命令:

yarn add pinia
  # 或者使用 npm
  npm install pinia

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

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

<!-- main.ts -->


    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");

3.2. 创建store

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

我们需要使用pinia提供的defineStore()方法来创建一个store,该store用来存放我们需要全局使用的数据。

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

/src/store/user.ts


  import { defineStore } from 'pinia'


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


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

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

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

3.3. 使用store

前面我们创建了一个store,说白了就是创建了一个方法,那么我们的目的肯定是使用它,假如我们要在App.vue里面使用它,该如何使用呢?

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

使用store很简单,直接引入我们声明的useUsersStore 方法即可,我们可以先看一下执行该方法输出的是什么:

3.4. 添加state

我们都知道store是用来存放公共数据的,那么数据具体存在在哪里呢?前面我们利用defineStore函数创建了一个store,该函数第二个参数是一个options配置项,我们需要存放的数据就放在options对象中的state属性内。

假设我们往store添加一些任务基本数据,修改user.ts代码。

export const useUsersStore = defineStore("users", {
  state: () => {
  return {
  name: "老铁666",
  age: 23,
  sex: "男",
  };
  },
  });

上段代码中我们给配置项添加了state属性,该属性就是用来存储数据的,我们往state中添加了3条数据。需要注意的是,state接收的是一个箭头函数返回的值,它不能直接接收一个对象。

3.5. 操作state

我们往store存储数据的目的就是为了操作它,那么我们接下来就尝试操作state中的数据。

3.5.1. 读取state数据

读取state数据很简单,前面我们尝试过在App.vue中打印store,那么我们添加数据后再来看看打印结果:

这个时候我们发现打印的结果里面多了几个属性,恰好就是我们添加的数据,修改App.vue,让这几个数据显示出来。

<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>

输出结果:

上段代码中我们直接通过store.age等方式获取到了store存储的值,这样做还是比较繁琐的,我们其实可以用解构的方式来获取值,使得代码更简洁一点。

解构代码如下:

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

上段代码实现的效果与一个一个获取的效果一样,不过代码简洁了很多。

3.5.2. 多个组件使用state

我们使用store的最重要的目的就是为了组件之间共享数据,那么接下来我们新建一个child.vue组件,在该组件内部也使用state数据。

child.vue代码如下:

<template>
  <h1>我是child组件</h1>
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
  <p>性别:{{ sex }}</p>
</template>
<script setup lang="ts">
  import { useUsersStore } from "../src/store/user";
  const store = useUsersStore();
  const { name, age, sex } = store;
</script>

child组件和app.vue组件几乎一样,就是很简单的使用了store中的数据。

实现效果:

这样就实现了多组件同时使用store中的数据。

3.5.3. 修改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>

上段代码新增了changeName 方法,改变了store中name的值,我们点击按钮,看看最终效果:

我们可以看到store中的name确实被修改了,但是页面上似乎没有变化,这说明我们的使用的name不是响应式的。

想要让name等属性变为响应式的,其实pinia给我们提供了方法:

storeToRefs函数:

  • 用法:storeToRefs(store)
  • 作用:将 pinia 中的 store 对象中的状态转换为具有 .value 的 ref 对象集合。

利用pinia的storeToRefs函数,可以将state中的数据变为了响应式的。

app.vue和child.vue代码修改如下:

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

child.vue代码如下:

<template>
  <h1>我是child组件</h1>
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
  <p>性别:{{ sex }}</p>
  <button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
  import { useUsersStore } from "../src/store/user";
  import { storeToRefs } from 'pinia';
  const store = useUsersStore();
  const { name, age, sex } = storeToRefs(store);
  const changeName = () => {
    store.name = "小猪课堂";
  };
</script>

修改为storeToRefs函数后,实现效果如下:

现在store中数据就是响应式的了!

3.5.4. 重置state

有时候我们修改了state数据,想要将它还原,这个时候该怎么做呢?就比如用户填写了一部分表单,突然想重置为最初始的状态。

$reset()方法:

  • $reset() 方法是用于重置状态的方法。当调用 $reset() 方法时,它将会将状态对象恢复到初始状态。

此时,我们直接调用store的$reset()方法即可,继续使用我们的例子,添加一个重置按钮。

代码如下:

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

当我们点击重置按钮时,store中的数据会变为初始状态,页面也会更新。

3.5.5. 批量更改state数据

前面我们修改state的数据是都是一条一条修改的,比如store.name="张三"等等,如果我们一次性需要修改很多条数据的话,有更加简便的方法。

$patch方法:

  • $patch 方法是用于局部更新状态的方法。它允许您通过传递一个包含要更新的属性的对象来更新状态。可用于批量修改数据。

使用store的$patch方法,修改app.vue代码,添加一个批量更改数据的方法。

代码如下:

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

但是采用这种批量更改的方式似乎代价有一点大,假如我们state中有些字段无需更改,但是按照上段代码的写法,我们必须要将state中的所有字段例举出了。

所以,为了解决该问题,pinia提供的$patch方法还可以接收一个回调函数,它的用法有点像我们的数组循环回调函数了。


示例代码如下:

store.$patch((state) => {
  state.items.push({ name: 'shoes', quantity: 1 })
  state.hasChanged = true
})

上段代码中我们即批量更改了state的数据,又没有将所有的state字段列举出来。

3.5.6. 直接替换整个state

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

$state方法:

  • 用于获取状态的方法。它允许直接访问 store 实例的状态对象。

注意:

  • $state 是一个只读属性,用于获取状态的当前值。
  • 对 $state 的访问不会触发响应式更新。

示例代码:

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

上段代码会将我们提前声明的state替换为新的对象。

这种使用$state 方法完全替换掉 store 的 state的方法,不建议在生产中使用。具体原因如下:

  • 丢失响应性:使用 $state 方法替换整个状态对象会丢失 Vue 的响应系统的能力。Vue 无法检测到状态的变化,导致相关组件无法自动重新渲染,可能导致界面不同步或不正确的行为。
  • 组件无法依赖更新:当使用 $state 方法替换整个状态对象时,组件将无法依赖状态的特定属性进行更新。组件通常会使用计算属性、监听器或侦听器等方式来响应状态的变化,但这些机制将失效。
  • 非标准做法:使用 $state 方法替换整个状态对象违反了常见的状态管理模式和最佳实践。在大多数状态管理库中,包括 Pinia,都不鼓励或提供直接替换整个状态对象的方法。
  • 可维护性和调试困难:使用 $state 方法替换整个状态对象会使代码更难以维护和调试。在替换状态对象时,您需要确保新对象与原始状态对象具有相同的属性结构,以避免出现错误或未定义的行为。
  • 可能导致性能问题:由于无法使用 Vue 的响应系统来进行状态更新的优化和批处理,使用 $state 方法替换整个状态对象可能会导致性能下降,特别是在具有大量数据或频繁更新的情况下

综上所述,建议使用合适的方式来更新状态对象的属性,而不是使用 $state 方法完全替换整个状态对象。这样可以保持响应性、遵循最佳实践,并确保代码的可维护性和性能。

3.6. getters属性

在 Pinia 中,getters 属性是用于定义和访问 store 的计算属性的对象。该对象里面是各种各样的方法。可以把getter想象成Vue中的计算属性,它的作用就是返回一个新的结果,同样的它和计算属性一样也是会被缓存的。

3.6.1. 添加getter

我们先来看一下如何定义getter吧,修改user.ts。

代码如下:

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

上段代码中我们在配置项参数中添加了getter属性,该属性对象中定义了一个getAddAge方法,该方法会默认接收一个state参数,也就是state对象,然后该方法返回的是一个新的数据。

3.6.2. 使用getter

我们在store中定义了getter,那么在组件中如何使用呢?使用起来非常简单,我们修改App.vue。

代码如下:

<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>

这段代码中我们直接在标签上使用了store.gettAddAge方法,这样可以保证响应式,其实我们state中的name等属性也可以以此种方式直接在标签上使用,也可以保持响应式。

当我们点击批量修改数据按钮时,页面上的新年龄字段也会跟着变化。

3.6.3. getter中调用其它getter

前面我们的getAddAge方法只是简单的使用了state方法,但是有时候我们需要在这一个getter方法中调用其它getter方法,这个时候如何调用呢?

其实很简单,我们可以直接在getter方法中调用this,this指向的便是store实例,所以理所当然的能够调用到其它getter。

示例代码如下:

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "老铁666",
      age: 23,
      sex: "男",
    };
  },
  getters: {
    getAddAge: (state) => {
      return state.age + 100;
    },
    getNameAndAge(): string {
      return this.name + this.getAddAge; // 调用其它getter
    },
  },
});

这段代码中我们又定义了一个名为getNameAndAge的getter函数,在函数内部直接使用了this来获取state数据以及调用其它getter函数。

注意:这里没有使用箭头函数的形式,这是因为我们在函数内部使用了this,箭头函数的this指向问题可以移步到我笔记的(箭头函数的this指向问题)一章!

那么在组件中调用的形式没什么变化,代码如下:

<p>调用其它getter:{{ store.getNameAndAge }}</p>

3.6.4. getter传参

既然getter函数做了一些计算或者处理,那么我们很可能会需要传递参数给getter函数,但是我们前面说getter函数就相当于store的计算属性,和vue的计算属性差不多,那么我们都知道Vue中计算属性是不能直接传递参数的,所以我们这里的getter函数如果要接受参数的话,也是需要做处理的。

示例代码:

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

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

接下来我们在组件中使用,方式很简单,代码如下:

<p>新年龄:{{ store.getAddAge(1100) }}</p>

3.7. actions属性

前面我们提到的state和getters属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data数据和computed计算属性一样。

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

actions属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。

3.7.1. 添加actions

我们可以尝试着添加一个actions方法,修改user.ts。

代码如下:

export const useUsersStore = defineStore("users", {
  state: () => {
    return {
      name: "老铁666",
      age: 23,
      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方法,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。大家把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store。

3.7.2. 使用actions

使用actions中的方法也非常简单,比如我们在App.vue中想要调用该方法。

代码如下:

const saveName = () => {
  store.saveName("我是老铁");
};

我们点击按钮,直接调用store中的actions方法即可。

4. 总结示例代码

前面的章节中的代码都不完整,主要贴的是主要代码部分,我们这节将我们本篇文章用到的所有代码都贴出来,供大家练习。

main.ts代码:

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");

user.ts代码:

import { defineStore } from "pinia";


  // 第一个参数是应用程序中 store 的唯一 id
  export const useUsersStore = defineStore("users", {
    state: () => {
      return {
        name: "老铁666",
        age: 23,
        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;
      },
    },
  });

App.vue代码:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
  <p>性别:{{ sex }}</p>
  <p>新年龄:{{ store.getAddAge(1100) }}</p>
  <p>调用其它getter:{{ store.getNameAndAge }}</p>
  <button @click="changeName">更改姓名</button>
  <button @click="reset">重置store</button>
  <button @click="patchStore">批量修改数据</button>
  <button @click="saveName">调用aciton</button>

  <!-- 子组件 -->
  <child></child>
</template>
<script setup lang="ts">
import child from "./child.vue";
import { useUsersStore } from "../src/store/user";
import { storeToRefs } from "pinia";
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
const changeName = () => {
  store.name = "张三";
  console.log(store);
};
// 重置store
const reset = () => {
  store.$reset();
};
// 批量修改数据
const patchStore = () => {
  store.$patch({
    name: "张三",
    age: 100,
    sex: "女",
  });
};
// 调用actions方法
const saveName = () => {
  store.saveName("我是老铁");
};
</script>

child.vue代码:

<template>
  <h1>我是child组件</h1>
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
  <p>性别:{{ sex }}</p>
  <button @click="changeName">更改姓名</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
import { storeToRefs } from 'pinia';
const store = useUsersStore();
const { name, age, sex } = storeToRefs(store);
const changeName = () => {
  store.name = "老铁666";
};
</script>

5. 总结

pinia的知识点很少,如果你有Vuex基础,那么学起来更是易如反掌。其实我们更应该关注的是它的函数思想,大家有没有发现我们在Vue3中的所有东西似乎都可以用一个函数来表示,pinia也是延续了这种思想。

所以,大家理解这种组合式编程的思想更重要,pinia无非就是以下3个大点:

  • state
  • getters
  • actions

当然,本篇文章只是讲解了基础使用部分,但是在实际工作中也能满足大部分需求了,如果还有兴趣学习pinia的其它特点,比如插件、订阅等等,可以移步官网:pinia官网

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Vue3是一种流行的JavaScript框架,用于构建用户界面。Vue Router是Vue.js的官方路由器,用于实现单页面应用程序的导航功能。Vite是一个快速的Web开发构建工具,具有即时重载和快速的开发体验。Pinia是一个简单、快速、类型安全的状态管理库。 使用Vue3、Vue Router、Vite和Pinia进行组件化开发实战入门非常简单。 首先,我们需要安装Vue3和相关的库。可以使用npm或yarn来安装它们。在项目的根目录中运行以下命令: ``` npm install vue@next vue-router@next @pinia/vue@next vite --save ``` 安装完成后,我们可以开始创建一个Vue应用程序。在项目的根目录中创建一个`src`文件夹,并在其中创建一个`main.js`文件。在`main.js`文件中,我们需要导入VueVue Router和Pinia,并创建一个Vue实例。代码示例如下: ```javascript // main.js import { createApp } from 'vue'; import { createRouter, createWebHistory } from 'vue-router'; import { createPinia } from 'pinia'; // 导入组件 import App from './App.vue'; // 创建路由 const router = createRouter({ history: createWebHistory(), routes: [ // 定义路由 { path: '/', component: Home } ] }); // 创建 Pinia 实例 const pinia = createPinia(); // 创建 Vue 实例 const app = createApp(App); // 使用路由和 Pinia app.use(router); app.use(pinia); // 挂载应用 app.mount('#app'); ``` 现在,我们可以创建一个简单的Vue组件。在`src`文件夹中创建一个名为`App.vue`的文件,并在其中定义一个组件。在组件中,我们可以使用Pinia来管理组件的状态,使用Vue Router来处理导航。 ```html <!-- App.vue --> <template> <div> <router-view></router-view> <button @click="increment">增加计数器</button> <p>{{ count }}</p> </div> </template> <script> import { defineComponent } from 'vue'; import { useCounter } from '../store'; export default defineComponent({ setup() { const counter = useCounter(); const increment = () => { counter.increment(); }; return { count: counter.count, increment }; } }); </script> ``` 在上述示例中,我们创建了一个Counter组件,其中包含一个用于增加计数器的按钮和一个显示计数器值的段落。我们使用Pinia的`useCounter`钩子来访问和修改计数器的状态。 最后,在项目的根目录中运行以下命令启动应用程序: ``` npm run dev ``` 以上就是使用Vue3、Vue Router、Vite和Pinia进行组件化开发实战入门的一个简单示例。通过深入学习这些工具和框架,您将能够构建更复杂、功能丰富的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丶前端切图仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值