Vue3 项目你需要用 Pinia 替代 Vuex4!

1,335 阅读5分钟

Vue3 项目你需要用 Pinia 替代 Vuex

Pinia 替代 Vuex4?你在说什么?此时你心中肯定会产生很多疑问,例如 Pinia 是什么?Pinia 有什么优势?诸如此类的问题,你给笔者五分钟,笔者给你一个满意的答案!

去年10月的一场前端早早聊中,尤大下场给大家答疑,其中有个问题是 pinia 会替代 vuex 吗?

很大概率,都是 core team 成员,讨论结果未来形态会很像 pinia,新东西都写在 pinia 里,如果开发一个新项目,如果使用 ts,推荐使用 pinia。

我相信尤大推荐胜过笔者的千言万语~~~

image.png

But,为了收获你宝贵的一个赞,笔者还是做足了准备,请给笔者 5 分钟,我们一起探索 Pinia~~~

Pinia 介绍和五大优势

Vue3 自 2月7号 设置为默认版本后,预示着 Vue3 的开发生态已经完备,项目中可以安心的使用 Vue3 开发项目。Pinia 则是尤大推荐配合 Vue3 使用的状态管理工具,而且 Pinia 的开发团队也是 Vue 的核心成员,并且下一代 Vuex5 很多提案都包括了 Pinia 的几个优势。

五大优势

  • 可以对 Vue2 和 Vue3 做到很好的支持
  • 完美支持 TypeScript
    • 无需像 Vuex 4 自定义复杂的类型来支持 typescript,天生具备完美的类型推断
  • 更加简洁的语法,完美支持 Vue3 的 Composition api
    • 解决了代码反复横跳的问题,Pinia 与 Vue devtools 挂钩,不会影响 Vue 3 开发体验
  • 抛弃了 Mutations 的操作,只有 state、getters 和 actions
    • 极大的简化了状态管理库的使用,让代码编写更加容易直观
  • 模块化设计,无嵌套结构,体积很小不超过 1kb
    • 你引入的每一个 store 在打包时都可以自动拆分,且可以交叉组合使用 store

如果你说这五点有点太多了,记不住。可以简单总结 Pinia 的优势就是,更加简洁的语法完美支持 Vue3 的 Composition api对 TypeScript 的完美支持

image.png

Pinia 的安装与使用

纸上得来终觉浅,绝知此事要躬行。笔者建议学习时,手动敲印象会更深刻哦~~~

使用示例

安装环境

// 安装 vite 项目
npm init vite@latest
// 安装 Pinia 包 当前版本 2.0.11
npm install pinia@latest

注册 Pinia

// main.ts 注册 pinia
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia';

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

新建仓库

// src/store/index.ts
import { defineStore } from 'pinia';

export const mainStore = defineStore('main', {
  state: () => {
    return {
      helloWorld: 'hello world!'
    }
  },
  getters: {},
  actions: {}
});

// composition API 写法
export const mainStore = defineStore('main', () => {
  const helloWorld = ref('hello world!')
  return { helloWorld }
})
  • defineStore() 定义一个仓库。
    • defineStore( )方法的第一个参数:相当于为容器起一个名字。注意:这里的名字必须唯一,不能重复。这个是官方特别说明的一个事情。
    • defineStore( )方法的第二个参数:可以简单理解为一个配置对象,里边是对容器仓库的配置说明。当然这种说明是以对象的形式。
  • state 属性:用来存储全局的状态的,这里边定义的,就可以是为 SPA 里全局的状态了。
  • getters 属性:用来监视或者说是计算状态的变化的,有缓存的功能。
  • actions 属性:对 state 里数据变化的业务逻辑,需求不同,编写逻辑不同。说白了就是修改 state 全局状态数据的。

使用

<template>
  <h2>{{ store.helloWorld }}</h2>
  <h2>{{ helloWorld }}</h2>
</template>

<script lang='ts' setup>
import { mainStore } from '../store/index';
import { storeToRefs } from 'pinia';

// 执行这个实例
const store = mainStore()
// 如果是解构 需要使用 storeToRefs 不然就会失去响应式
const { helloWorld } = storeToRefs(store)
</script>

getters

如果你在获取 State 的值时作一些处理,比如处理手机号中间四位,那么就可以使用 getters,并且 getters 是有缓存特性的。

// store/index.ts
export const mainStore = defineStore('main', {
  state: () => {
    return {
      phoneNumber: 18797890000
    }
  },
  getters: {
    phoneHidden(state) {
      console.log('PhoneHidden被调用了');
      return state.phoneNumber.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
    }
  },
  actions: {
    ...
  }
});

// 使用 => <h2>{{ store.phoneHidden }}</h2> 这样界面上显示的数据就是 187****0000

修改状态数据的方式

直接修改

<h2>{{ store.count }}</h2>

// 点击按钮就能更新
const handleClick = function () {
  store.count++
}

使用 $patch

如果是多条数据,推荐使用$patch$patch的方式是经过优化的,会加快修改速度,对程序的性能有很大的好处。所以如果你是多条数据同时更新状态数据,推荐使用 $patch 方式更新。

const handleClick = function () {
  store.$patch({
    count: store.count + 1,
    helloWorld: store.helloWorld === 'Pinia' ? 'Vue3' : 'Pinia'
  })
}

使用 action

在 actions 中写好逻辑 再调用 actions

const handleClick = function () {
  store.changeState()
}
// store/index
export const mainStore = defineStore('main', {
  state: () => {
    return {
      helloWorld: 'Pinia',
      count: 0,
      phoneNumber: 18797894671
    }
  },
  getters: {
    // 在获取 State 的值时作一些处理
    // Getters 是有缓存特性的
    phoneHidden(state) {
      console.log('PhoneHidden被调用了');
      return state.phoneNumber.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
    }
  },
  actions: {
    // 不能使用箭头函数 因为需要使用 this 
    // 箭头函数绑定的是 外部的 this
    changeState() {
      this.count++
      this.helloWorld = 'Leiloloaa'
    },
  }
});

store 的相互调用

项目中可能有多个 store,pinia 支持相互间的调用

// 在 store 文件夹下 新建 goods.ts
import { defineStore } from 'pinia';

export const goodsStore = defineStore('goods', {
  state: () => {
    return {
      list: ['vue3', 'pinia', 'vuex']
    }
  }
})
// store/index.ts
import { defineStore } from 'pinia';
import { goodsStore } from './goods';

export const mainStore = defineStore('main', {
  state: () => {
    return {}
  },
  getters: {},
  actions: {
    getList() {
      console.log(goodsStore().list);
    }
  }
});

ENDING

使用 Pinia 之后,Vuex 就别再来了~~~ 希望本文能够帮助到屏幕面前你,不赶时间的话,可以给笔者点个赞吗?手动比心~


阮一峰推荐的 Vue3 源码学习利器 mini-vue,我已经上车,若是想学习 Vue3 源码课程,可扫描下方二维码咨询!

image.png

学习 mini-vue 你能得到什么?

  • 方便阅读 Vue3 源码
    • 命名相同,实现方式相同
  • TDD 单元测试
  • 重构代码、位运算等
  • 后续的分享学习服务
    • 社群解答,同学分享 等等 好处