vue3 基础-Pinia 可能替代 Vuex 的全局数据状态管理

发布时间 2023-11-19 22:40:32作者: 致于数据科学家的小陈

Pinia 初体验

Pinia.js是由Vue.js团队核心成员开发的新一代状态管理器,使用Composition Api进行重新设计的,也被视为下一代Vuex。

Pinia是一个Vue的状态管理库,允许跨组件、跨页面进行全局共享状态,也由于其设计的简洁性、和对typescript的良好支持,取代Vuex指日可待。

或许,你在想在vue3中Composition API完全可以使用响应式变量进行全局共享状态,为什么还需要Pinia呢?其实你忽略的一点,你这样做在单页面进行应用是完全可以的,但是如果页面时服务端进行渲染呈现可能就有问题了.

安装

npm install pinia

引入到项目

在 main.js 中只需要调用createPinia()方法将pinia实例化,然后挂载到vue实例上就可以了.

import { createApp } from 'vue'
import App from './App.vue'

import { createPinia } from 'pinia'

const app = createApp(App)

const pinia = createPinia()
app.use(pinia)

app.mount('#app')

下一步就应该去创建我们的数据仓库store了,store中存储的是我们需要共享的数据.

创建store需要调用pinia中的defineStore方法,该方法接受两个参数,第一个参数是store的唯一 id,第二个参数是store的配置属性.

defineStore方法配置store属性时,有两种写法,一种是Option 对象形式,一种是Setup 函数形式。

对于 Option 的方式和 Vuex 是一样的, 区别在于, 取消了 Mutations 而让 Actions 同时能进行同步和异步的操作, 这很方便.

在 src 目录下创建一个 store / index.js 的文件:

import { defineStore } from 'pinia'

// 模块1
export const useStore = defineStore("store", {
  state: () => ({
    name: 'youge',
    age: 18
  }),

  // 同步 / 异步 都可以写在一起, 这比 Veux 方便多了
  actions: {
    changeName(name) {
      this.name = name
    },
    aysncChangeName(name) {
      setTimeout(() => {
        this.name = name
      }, 2000)
    }
  }
})

// 模块2
// export const useStore2 = defineStore("store2", { })

我其实还是更偏向这种 vuex 风格写法的.

当然用 setup 的写法也支持的.

import { ref } from 'vue'
import { defineStore } from 'pinia'

// 模块1 
export const useStore = defineStore('store2', () => {

  // 原来的 state 数据直接进行定义即可, 主要要响应式
  let name = ref('youge')
  let age = ref(18)

  // 同步/异步任务, 直接定义成普通函数即可 
  const changeName = (newName) => name.value = newName

  function aysncChangeName(newName) {
    setTimeout(() => {
      name.value = newName
    }, 2000);
  }

  // 划重点! 这里一定要 return 出去哦
  return { name, changeName, aysncChangeName }
})

// 模块2
// export const useStore2 = defineStore("store2", () => { })

则对应的视图, 这里以 setup 的风格:

<template>
  <div>
    <p>同步获取 store 的 name 为: {{ name }}</p>
    <p>异步获取 store 的 name 为: {{ name }}</p>
  </div>
  <button @click="changeName('jack')">同步修改数据</button>
  <button @click="aysncChangeName('jack')">异步修改数据</button>
</template>

<script setup>
// import Father from './Father.vue'
import { useStore } from './store'
import { storeToRefs } from 'pinia'

const store = useStore()

// store 的方法, 直接解构使用就行
const { changeName, aysncChangeName } = store

// store 的变量就需要包装一下成响应式了
const { name } = storeToRefs(store)

</script>

如果用 setup 的写法呢, 需注意变量结构会丢失响应式, 需要用 pinia 提供的 storeToRefs 包装一下即可.

<template>
  <!-- <div>
    <Father />
  </div> -->

  <div>
    <p>同步获取 store 的 name 为: {{ name }}</p>
    <p>异步获取 store 的 name 为: {{ name }}</p>
  </div>
  <button @click="changeName('jack')">同步修改数据</button>
  <button @click="aysncChangeName('jack')">异步修改数据</button>
  <button @click="handleClick('jack')">直接修改数据</button>
</template>

<script setup>
// import Father from './Father.vue'
import { useStore } from './store'
import { storeToRefs } from 'pinia'

const store = useStore()
const { changeName, aysncChangeName } = store
// store 的变量就需要包装一下成响应式了
const { name } = storeToRefs(store)

const handleClick = (newName) => store.name = newName

</script>

或者直接用对象.属性的方式, 不通过结构也行的, 这样也不会丢失响应式.

<template>
  <!-- <div>
    <Father />
  </div> -->

  <div>
    <p>同步获取 store 的 name 为: {{ store.name }}</p>
    <p>异步获取 store 的 name 为: {{ store.name }}</p>
  </div>
  <!-- 1. 同步 / 异步 修改 -->
  <button @click="store.changeName('jack')">同步修改数据</button>
  <button @click="store.aysncChangeName('jack')">异步修改数据</button>
  <!-- 2. 直接在标签上改 -->
  <button @click="handleClick('jack')">直接修改数据</button>

</template>

<script setup>
import { useStore } from './store'

const store = useStore()
const handleClick = (newName) => store.name = newName

</script>

这真的是, 用 Pinia 感觉就和没有用一样丝滑.
不过这里也只是初步体验, 具体使用还是待考察中.