0


Pinia 上手指南 -- 新一代状态管理工具,它会成为 Vuex 的良好替代品吗?

一、简介

Pinia 是 Vue 的存储库,它允许你跨组件/页面共享状态。如果你熟悉 Composition API,你可能已经使用过简单的

export const state = reactive({}),

这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,则会将你的应用程序暴露给安全漏洞

即使在小型单页应用程序中,你也可以从使用 Pinia 中获得很多好处:

  • 开发工具支持 - 跟踪动作、突变的时间表- store只出现在使用它们的组件中- 时间旅行和更容易的调试
  • 热模块更新 - 在不重新加载页面的情况下修改你的store- 在开发时保持任何现有状态
  • 插件:使用插件扩展 Pinia 功能
  • 为 JS 用户提供适当的 TypeScript 支持或自动完成功能
  • 服务器端渲染(SSR)支持

Pinia 有以下特点:

  • 完整的 TypeScript 的支持;
  • 足够轻量,压缩后的体积约2kb;
  • 去除 mutations,只有 state,getters,actions;
  • actions 支持同步和异步;
  • 没有模块嵌套,只有 store 的概念,store 之间可以自由使用,更好的代码分割;
  • 无需手动添加 store,store 一旦创建便会自动添加;

二、入门

1、安装

yarn add pinia
// or with npm
npm install pinia

注意:

如果你的应用使用 Vue2,你还需要安装composition api:

@vue/composition-api;

如果你使用 Nuxt,则应遵循这些说明;如果你使用的是 Vue CLI,你可以试试这个非官方的插件。

2、何为 Store ?

一个 Store(如 Pinia)是一个实体,它持有未绑定到你的组件树的状态和业务逻辑。换句话说,它托管全局状态。它有点像一个始终存在并且每个人都可以读取和写入的组件。它包含三个概念state、**getters **和 actions,并且可以假设这些概念等同与 data、computed 和 methods 在组件中。

3、何时使用 Store ?

Store 应该包含可以在整个应用程序中访问的数据。这包括在许多地方使用的数据,例如在导航栏中显示的用户信息,以及需要通过页面保存的数据,如非常复杂的多步骤表单。

另一方面,你应该避免在 Store 中包含可能托管在组件中的本地数据,例如页面本地元素的可见性。并非所有应用程序都需要访问全局状态,但如果你需要,Pania 将使你能更轻松地完成。

三、基本使用

1、Store

在 main.js 中创建并使用

import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';

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

2、State

定义 state

在 src/pinia 下面创建一个 count.js

import { defineStore } from 'pinia'

export const useStore = defineStore({
  id: 'count',   // id必填,且需要唯一
  state: () => {
    return {
      count: 0
    }
  }
})

// 另外一种方式
export const useStore = defineStore('count', {   // 将id作为第一个参数
  state: () => {
    return {
      count: 0
    }
  }
})

获取 state

<template>
  <div>
    <p>{{ countStore.count }}</p>
  </div>
</template>

<script>
import { useStore } from "../pinia/count.js";
export default {
  setup() {
    let countStore = useStore();

    return {
      countStore
    };
  }
}
</script>

也可以结合 computed 获取

let count = computed(() => countStore.count)

state 也可以使用解构,但使用解构会使其失去响应式,这时候可以用 pinia 的 storeToRefs

import { storeToRefs } from 'pinia';
let { count } = storeToRefs(countStore);

重置 state

可以通过调用 store 上的方法将 state 重置为其初始值:

$reset()
let countStore = useStore();

countStore.$reset();

更换 state

可以通过将 store 中的

$state

属性设置为新对象来替换 store 的整个 state

let countStore = useStore();

countStore.$state = { count: 0 };

修改 state

可以如下直接修改 state

let countStore = useStore();

countStore.count++

但一般不建议这么做,而是通过 actions 去修改 state,actions 里可以直接通过 this 访问

// count.js
export const useStore = defineStore({
  id: 'count',
  state: () => {
    return {
      count: 0
    }
  },
  actions: {   // 建议通过action修改state,更符合业务逻辑
    increment() {
      this.count++
    }
  }
})
<script>
import { useStore } from "../pinia/count.js";
export default {
  setup() {
    let countStore = useStore();
    countStore.increment();   // 通过actions修改state,更符合业务逻辑

    return {
      countStore
    };
  }
}
</script>

订阅(监听)state

可以通过

$subscribe()

观察 state 及其变化,类似于 Vuex 的subscribe 方法

let countStore = useStore();

countStore.$subscribe((mutation, state) => {
  console.log(mutation);
  console.log("监听count变化触发的回调,count的值为:", state.count);
  count = state.count;
});

// 可以在监听到state变化后执行某些操作

当 state 变化时,控制台打印

3、Getters

创建 getters

export const useStore = defineStore({
  id: 'count',
  state: () => {
    return {
      count: 0
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  }
})

使用其他 getters

大多数时候,getters 只会依赖状态,但是有时候可能需要使用其他 getters

export const useStore = defineStore({
  id: 'count',
  state: () => {
    return {
      count: 0
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2;
    },
    doublePlusOne() {
      return this.doubleCount + 1;
    }
  }
})

可以直接在 countStore 实例上访问 getters

<template>
  <div>
    <p>{{ countStore.doubleCount }}</p>
    <p>{{ countStore.doublePlusOne }}</p>
  </div>
</template>

访问其他 store 中的 getters

import { useOtherStore } from './other-store'

export const useStore = defineStore({
  id: 'count',
  state: () => ({
    // ...
  }),
  getters: {
    otherGetter(state) {
      let otherStore = useOtherStore();
      return state.count + otherStore.data;
    },
  },
})

4、Actions

创建 actions

export const useStore = defineStore({
  id: 'count',
  state: () => {
    return {
      count: 0
    }
  },
  actions: {
    increment() {
      this.count++;
    },
    randomCount() {
      this.count = Math.round(100 * Math.random());
    }
  }
})

像 getters 一样,actions 通过完全输入(和自动完成)支持访问整个 store 实例。与它们不同的是,它可以是异步的。

异步 actions

actions 可以像写一个简单的函数一样支持 async/await 的语法,让你愉快地应付异步处理的场景

export const useStore = defineStore({
  id: 'login',
  actions: {
    async login(account, pwd) {
      let { data } = await api.login(account, pwd);
      return data;
    }
  }
})

访问其他 store 中的 actions

import { useAuthStore } from './auth-store'

export const useSettingsStore = defineStore({
  id: 'settings',
  state: () => ({
    // ...
  }),
  actions: {
    async fetchUserPreferences(preferences) {
      let authStore = useAuthStore();
      if (authStore.isAuthenticated) {
        this.preferences = await fetchPreferences();
      } else {
        throw new Error('User must be authenticated');
      }
    }
  }
})

订阅 actions

可以使用

store.$onAction()

观察 actions 及其结果。传递给它的回调在操作本身之前执行。

after

处理承诺并允许你更改操作的返回值。

onError

允许你阻止错误传播。这些对于在运行时跟踪错误很有用,类似于Vue 文档中的这个技巧。

这是一个在运行操作之前和它们解决/拒绝之后记录的示例

// 订阅(监听)actions
  store.$onAction(
    ({
      name,   // name of the action
      store,   // store instance, same as `someStore`
      args,   // array of parameters passed to the action
      after,   // hook after the action returns or resolves
      onError,   // hook if the action throws or rejects
    }) => {      
      console.log("name:",name);
      console.log("store:",store);
      console.log("before store.count:",store.count);
      console.log("args",args);
      // this will trigger if the action succeeds and after it has fully run.
      // it waits for any returned promised
      after(() => {
        console.log("after store.count:",store.count);
      });

      // this will trigger if the action throws or returns a promise that rejects
      onError((error) => {
        console.log(error);
      });
    }
  );

当触发 actions 时,控制台打印

默认情况下,操作订阅绑定到添加它们的组件(如果 store 位于组件的 内部

setup()

)。意思是,当组件被卸载时,它们将被自动删除。如果要在卸载组件后保留它们,请将

true

作为第二个参数传递来把操作订阅与当前组件分离

export default {
  setup() {
    let someStore = useSomeStore();

    // this subscription will be kept after the component is unmounted
    someStore.$onAction(callback, true)

    // ...
  },
}

四、与 Vuex 的比较

1、优缺点

Vuex 的优点

  • 支持调试功能,如时间旅行和编辑
  • 适用于大型、高复杂度的Vue.js项目

Vuex 的缺点

  • 从 Vue 3 开始,getter 的结果不会像计算属性那样缓存
  • Vuex 4有一些与类型安全相关的问题

Pinia 的优点

  • 完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
  • 极其轻巧(体积约 1KB)
  • store 的 action 被调度为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数,这在 Vuex 中很常见
  • 支持多个Store
  • 支持 Vue devtools、SSR 和 webpack 代码拆分

Pinia 的缺点

  • 不支持时间旅行和编辑等调试功能

2、使用场景

由于Pinea是轻量级的,体积很小,它比较适合中小型应用。它也适用于低复杂度的Vue.js项目,因为调试功一些能,如时间旅行和编辑仍然不被支持。

将 Vuex 用于中小型 Vue.js 项目是过度的,因为它重量级的,对性能降低有很大影响。因此,Vuex 适用于大规模、高复杂度的 Vue.js 项目。

据Vue.js 核心团队成员并积极参与 Vuex 设计的 Pinia 的创建者(Eduardo San Martin Morote)所说,Pania 和 Vuex 的相似之处多于不同之处:

Pinia 试图尽可能地接近 Vuex 的理念。它的设计是为了测试 Vuex 的下一次迭代的建议,它是成功的,因为我们目前有一个开放的 RFC,用于 Vuex5,其API与 Pinea 使用的非常相似。我对这个项目的个人意图是重新设计使用全局 store 的体验,同时保持 Vue 的平易近人的理念。我保持 Pinea 的API与 Vuex 一样接近,因为它不断向前发展,使人们很容易迁移到Vuex,甚至在未来融合两个项目(在Vuex下)。

五、总结

以上就是关于 Pinia 的一些基本介绍,Pinia 的内容远远不止这些,对于我们使用者而言,首先掌握最基本用法,然后再去深入更多的用法,如在 Pinia 中使用插件来扩展功能等。

更多内容请前往官网了解

Piniahttps://pinia.vuejs.org/欢迎大家一键三连,一起学前端,一起进步~~~

标签: pinia vue javascript

本文转载自: https://blog.csdn.net/qq_41809113/article/details/122672994
版权归原作者 前端不释卷leo 所有, 如有侵权,请联系我们删除。

“Pinia 上手指南 -- 新一代状态管理工具,它会成为 Vuex 的良好替代品吗?”的评论:

还没有评论