vue3了,我决定试试Pinia

515 阅读4分钟

Pinia(皮尼亚)介绍

官方描述: The Vue Store that you will enjoy using

  • Pinia 是一个轻量级的用于 Vue 的状态管理库,允许跨组件/页面共享状态。类似 Vuex, 是 Vue 的另一种状态管理工具
  • Pinia 支持 Vue2 和 Vue3

Pinia文档有这么一段话

图片

翻译过来:

Pinia 试图尽可能接近 Vuex 的理念。它旨在测试 Vuex 下一次迭代的提案,并且取得了成功,因为我们目前有一个针对 Vuex 5 的开放式 RFC,其 API 与[1]Pinia 使用的 API 非常相似[2]。请注意,Pinia 的作者 I (Eduardo) 是 Vue.js 核心团队的一员,并积极参与 Router 和 Vuex 等 API 的设计。我个人对这个项目的意图是重新设计使用全局 Store 的体验,同时保持 Vue 平易近人的哲学。我让 Pania 的 API 与 Vuex 一样接近,因为它不断向前发展,使人们可以轻松地迁移到 Vuex,甚至在未来融合这两个项目(在 Vuex 下)。

Vuex4一直被人诟病对于typescript的支持不良好,Vuex5也迟迟未来,所以有了Pinia的出现,所以现在学习Pinia,相当于提前学习Vuex5。还有什么理由不学呢?卷起来。

本文主要讲 Pinia 在 Vue3 中的使用, 在 Vue2 中使用略有差异,遇到的时候我也会同时列出来,更具体的使用,需要您查看Pinia官方文档

Pinia 优势

  • 文档齐全,容易上手学习

  • 极轻, 仅有 1 KB

  • 模块化设计,便于拆分状态

  • 支持多个Store

  • 支持 Vue devtools、SSR 和 webpack 代码拆分

  • 没有模块module的概念,不用面对一个store下嵌套着许多模块,使用单文件store(有点类似redux/toolkit的一个reducer),可以直接导入其他store使用

  • 减去了mutations的概念,只保留state,getters和anctions三个概念,减少代码冗余

  • Pinia无需创建复杂的包装器来支持typescript,对于typescript类型判断是完全支持的,享受ide带来的自动补全,减少开发的心智负担,减少vue开发过程中的面向字符串编程

使用方法

1. 安装

yarn add pinia
# or with npm
npm install pinia

如果你的项目使用的是Vue2,需要另外安装composition api: @vue/composition-api。如果使用 了Nuxt,则应遵循这些说明

如果你使用的是 Vue CLI,你可以试试这个非官方插件

2.使用

Pinia是一个围绕Vue 3 Composition API的封装器。因此,你不必把它作为一个插件来初始化,除非你需要Vue devtools支持、SSR支持和webpack代码分割的情况:

创建一个 pinia(根存储)并将其传递给应用程序:

import { createPinia } from 'pinia'

app.use(createPinia())

使用上面的代码,将Pinia添加到Vue.js项目中,这样就可以在你的代码中使用全局的Pinia对象。

如果使用的是 Vue 2,还需要安装一个插件并将 created 注入pinia到应用程序的根目录:

import { createPinia, PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
  el'#app',
  // other options...
  // ...
  // note the same `pinia` instance can be used across multiple Vue apps on
  // the same page
  pinia,
})

3.核心概念

创建store

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state() => {
    return { count0 }
  },
  // could also be defined as
  // state: () => ({ count: 0 })
  actions: {
    increment() {
      this.count++
    },
  },
})

我们也可以使用函数(类似于组件setup())的方式来定义 Store

export const useCounterStore = defineStore('counter'() => {
  const count = ref(0)
  function increment() {
    count.value++
  }

  return { count, increment }
})

在组件中使用store

import { useCounterStore } from '@/stores/counter'
export default {
  setup() {
    const counter = useCounterStore()

    counter.count++
    // with autocompletion ✨
    counter.$patch({ count: counter.count + 1 })
    // or using an action instead
    counter.increment()
  },
}

还可以以Composition Api形式用

// Composition Api
import { computed } from 'vue'
import { useCounterStore } from '../stores/counter'
export default {
  name'HelloWorld',
  setup() {
    const counter = useCounterStore()
    return {
      // state 和 getter 都需要在使用 computed,这和 Options Api 一样
      countcomputed(() => counter.count),
      increment() => { counter.count++ }, // 可以直接修改 state 的值
      increment: counter.increment// 可以引用 store 中定义的 action
    }
  }
}

如果你不了解Composition API也没关系,Pinia可直接使用官方提供的 mapActions 和 mapState 方法,导出 store 中的 state、getter、action其用法与 Vuex 基本一致,很容易上手

//Options Api
const useCounterStore = defineStore('counter', {
  state() => ({ count0 }),
  getters: {
    double(state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    }
  }
})

const useUserStore = defineStore('user', {
  // ...
})

export default {
  computed: {
    // other computed properties
    // ...
    // gives access to this.counterStore and this.userStore
    ...mapStores(useCounterStore, useUserStore)
    // gives read access to this.count and this.double
    ...mapState(useCounterStore, ['count''double']),
  },
  methods: {
    // gives access to this.increment()
    ...mapActions(useCounterStore, ['increment']),
  },
}

Pinia的用法非常的多,我相信总有一种方式适合你。

以下是一种常见的todolist示例

import { defineStore } from 'pinia'

export const todos = defineStore('todos', {
  state() => ({
    /** @type {{ text: string, id: number, isFinished: boolean }[]} */
    todos: [],
    /** @type {'all' | 'finished' | 'unfinished'} */
    filter'all',
    // type will be automatically inferred to number
    nextId0,
  }),
  getters: {
    finishedTodos(state) {
      // autocompletion! ✨
      return state.todos.filter((todo) => todo.isFinished)
    },
    unfinishedTodos(state) {
      return state.todos.filter((todo) => !todo.isFinished)
    },
    /**
     * @returns {{ text: string, id: number, isFinished: boolean }[]}
     */
    filteredTodos(state) {
      if (this.filter === 'finished') {
        // call other getters with autocompletion ✨
        return this.finishedTodos
      } else if (this.filter === 'unfinished') {
        return this.unfinishedTodos
      }
      return this.todos
    },
  },
  actions: {
    // any amount of arguments, return a promise or not
    addTodo(text) {
      // you can directly mutate the state
      this.todos.push({ text, idthis.nextId++, isFinishedfalse })
    },
  },
})

4.性能

Pinia和Vuex都非常快,在某些情况下,使用Pinia的web应用程序会比使用Vuex更快。这种性能的提升可以归因于Pinia的极轻的重量,Pinia体积约1KB。

Pinia的学习和使用是相当友好的,看一遍官方文档就能上手,在上手过程中可以明显地感受到相对于vuex更加快捷,编码体验优秀。如果现在有新项目要使用 Vue3 进行开发,推荐无脑使用 Pinia。

详细使用请参考: 官方Demo