Pinia 与 Vuex 使用区别

本文介绍了Pinia,一个轻量级且易于使用的Vue状态管理库,它支持options/api和composition/api,通过简化状态管理结构、减少资源浪费和提供更好的按需加载机制,与Vuex相比有更高的代码效率。文章还详细讲述了从Vuex向Pinia的迁移步骤和关键代码调整点。
摘要由CSDN通过智能技术生成

Pinia优势

  1. Pinia 是一个全新的 Vue 状态管理库
  2. Vue2 和 Vue3 都能支持
  3. options api 和 compostions api 都支持,维护成本低
  4. 抛弃传统的 Mutation,只有 state, getter 和 action ,简化状态管理库
  5. 不需要嵌套模块,符合 Vue3 的 Composition api,让代码扁平化
  6. TypeScript支持
  7. 代码简单,很好的代码自动分割
  8. 极轻,仅有 1 KB
  9. 代码自动分割这里需要说明一下

举例:某项目有 3 个 store(user、app、tab),2 个页面(首页、个人中心),首页用到 app store,个人中心用到 user 和 tab store

vuex 会把 3 个 store 合并并打包,当首页用到 Vuex 时,这个包会引入到首页一起打包,最后输出 1 个 js chunk
这样出现的问题是,首页只需要 1 个 store,但其他 2 个无关的 store 也被打包进来,造成资源浪费
解决方案:首页优化时会考虑到这个场景,一般处理方案是去做 vuex 的按需加载,beforeCreate 时,可以去判断当前页需要加载哪些 store,之后利用 vuex store 实例上的 registerModule 进行动态注册
pinia 在打包时会检查引用依赖,当首页用到 app store,打包只会把用到的 store 和页面合并输出 1 个 js chunk,其他 2 个 store 不会耦合在其中

迁移Pinia

官网:https://pinia.web3doc.top/

从 Vuex 迁移:Migrating from Vuex ≤4

其实在使用上区别不大,但是有几点需要改变:

如下示例为 Vuex4.x 和 Pinia 代码

没有mutations
Vuex 如何使用 actions ?Pinia 这里做了两点改变

第一个参数 context 被移除

// vuex index.js
import appModule from './modules/app'
const store = createStore({
  modules: {
    appModule
  }
})

// vuex modules/app.js
const useAppStore = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment (state, num) {
      state.count += num
    }
  },
  actions: {
    increment (context, num) {
      // 或是直接把 context 里的 commit 解构出来
      context.commit('increment', num)
    }
  }
})


不再使用 dispatch 调用 actions

// .vue 文件
this.$store.dispatch('app/increment', 2)


Pinia 如何使用 actions?

在 actions 里直接使用 this 获取到 state 的值

// pinia modules/app.js
const useAppStore = defineStore('app', {
  state: {
    count: 0
  },
  actions: {
    increment (num) {
      this.state.count += num
    }
  }
})

// .vue 文件
import useAppStore from '@/store/modules/app'
useAppStore().increment(2)


没有modules嵌套结构

# Vuex
src
└── store
    ├── index.js 
    └── modules
        ├── module1.js
        └── nested
            ├── index.js
            ├── module2.js
            └── module3.js

# Pinia
src
└── stores
    ├── index.js
    ├── module1.js
    ├── nested-module2.js
    ├── nested-module3.js
    └── nested.js


Vuex 需要有一个主要的 Store,最终会形成一个树形引用结构

Pinia 不再需要一个主要的 Store,是一个平面的结构,可创建不同的 Store

const useAppStore = defineStore('app', { /* ... */ })


注意:Pinia 每一个文件都需要有一个唯一的命名,类似于 Vuex 的命名空间(namespaced: true)

import appModule from './modules/app'
const store = createStore({
  modules: {
    appModule
  }
})

// vuex modules/app.js
const useAppStore = createStore({
  namespaced: true
})


getters用法改变
Vuex 里一个 getters 想使用其他 getters,需要借助其第二个参数

如下示例为:统计所有书折扣后的总价钱

const useBookStore = createStore({
  state() {
    return {
      books: [
        { name: 'book1', count: 3, price: 10 },
        { name: 'book2', count: 1, price: 20 },
        { name: 'book3', count: 2, price: 15 }
      ],
      discount: 0.9
    }
  },
  getters: {
    totalPrice(state, getters) {
      const totalPrice = state.books.reduce((acc, cur) => {
        return (acc += cur.count * cur.price)
      }, 0)
      return totalPrice * getters.currentDiscount
    },
    currentDiscount(state) {
      return state.discount
    }
  }
}


Pinia 去掉了第二个参数,可以在里面使用 this 取到其他 getters

const useBookStore = createStore({
  state() {
    return {
      books: [
        { name: 'book1', count: 3, price: 10 },
        { name: 'book2', count: 1, price: 20 },
        { name: 'book3', count: 2, price: 15 }
      ],
      discount: 0.9
    }
  },
  getters: {
    totalPrice(state) {
      const totalPrice = state.books.reduce((acc, cur) => {
        return (acc += cur.count * cur.price)
      }, 0)
      return totalPrice * this.currentDiscount
    },
    currentDiscount(state) {
      return state.discount
    }
  }
})


这里补充一点:由于 getters 是无法接受参数的,如果想要接受参数可以使用闭包

如下示例为:统计所有数量大于 2 的书折扣后总价钱(示例为 Vuex 的)

const useBookStore = createStore({
  getters: {
    totalPriceGreaterN(state, getters) {
      return n => {
        let totalPrice = 0
        const totalPrice = state.books.reduce((acc, cur) => {
          if (cur.count > n) {
            return (acc += cur.count * cur.price)
          } else {
            return acc
          }
        }, 0)
        return totalPrice * getters.currentDiscount
      }
    }
  }
}


在模板中可以这样使用(只是单方面举例)

<h2>{{ $store.getters.totalPriceGreaterN(2) }}</h2>

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值