Pinia快速入门-上手指南

Pinia是什么

PiniaVue 的存储库,允许您跨组件/页面共享状态。Pinia 这款产品最初是为了探索 Vuex 的下一个版本,整合了核心团队关于 Vuex 5 的许多想法。最终,我们意识到 Pinia 已经实现了我们想要在 Vuex 5 中提供的大部分内容,因此决定将其作为 新的官方推荐

Pinia特点

  • 足够轻量,Pinia 重约 1kb,甚至会忘记它的存在!
  • 去除 MutationActions 支持同步和异步(Actions一个顶俩,写起来简洁);
  • 无需手动注册 Store,Store 仅需要时才自动注册。如果从不使用,则永远不会“注册”(省心);
  • 没有模块嵌套,只有 Store 的概念,Store 之间可以自由使用,更好的代码分割;
  • Vue2Vue3 都能支持;
  • 支持大型项目迁移期间,PiniaVuex 混合使用(贴心迁移);
  • 更完美的 typescript 的支持;
  • Vue devtools 挂钩,Vue2 和 Vue3 开发体验更好;
  • 支持插件扩展功能;
  • 支持模块热更新,无需加载页面可以修改容器,可以保持任何现有的状态;
  • 支持服务端渲染;

VuexPinia 用哪个

Vuex 现在处于维护模式。它仍然可以工作,但不再添加新的功能。对于新的应用项目,建议使用 PiniaPinia 已经实现了我们想要在 Vuex 5 中提供的大部分内容,因此决定将其作为 新的官方推荐(注意:旧版网站没有更新)

如何使用 Pinia

一、安装

npm install pinia

vue2需要另外安装

npm i pinia @vue/composition-api --save

二、定义 Store

新建 src/stores 目录并在其下面创建 index.ts

Pinia 的目录通常被称为 stores 而不是 store, 这是为了强调 Pinia 使用多个 store,而不是 Vuex 中的单个 store,同时也有迁移期间 PiniaVuex 混合使用的考虑。

// src/stores/index.ts
// 引入Store定义函数
import { defineStore } from 'pinia'

// 定义Store实例并导出,useStore可以是任何东西,比如useUser, useCart
// 第一个参数,唯一不可重复,字符串类型,作为仓库ID 以区分仓库
// 第二个参数,以对象形式配置仓库的state,getters,actions
export const useStore = defineStore('main', {
  // state 推荐箭头函数,为了TS类型推断
  state: () => {
    return {
      name: '张三',
      counter: 0
    }
  },
  getters: {},
  actions: {}
})

在 main.ts 中引入并挂载到根实例。

// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 创建Vue应用实例
// 实例化 Pinia
// 以插件形式挂载Pinia实例
createApp(App).use(createPinia()).mount('#app')

vue2写法

定义store同vue3,main.js中引入并挂在到跟实例代码如下:

//main.js引入
// 引入pinia
import {createPinia,PiniaVuePlugin} from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()//需要挂载在实例上

new Vue({
  router,
  pinia,
  render: h => h(App)
}).$mount('#app')

vue2中如果报如下错误,


则需要在vue.config.js中增加以下配置:


三、State

1、访问State

<template>
  <div>
     <button @click="handleClick">修改状态数据</button>
     <!-- 模板内不需要加.value -->
     <p>{{store.name}}</p>
     <!-- computed获取 -->
     <p>{{name}}</p>
     <!-- 或者使用解构之后的 -->
     <p>{{counter}}</p>
  </div>
</template>

<script lang="ts" setup>
import { useStore } from '@/stores/index.ts'
// 使普通数据变响应式的函数  
import { storeToRefs } from "pinia";

const store = useStore()

// 结合computed获取
const name = computed(() => store.name)
// 解构并使数据具有响应式
const { counter } = storeToRefs(store);

// 点击 + 1;
function handleClick() {
  // ref数据这里需要加.value访问
  counter.value++;
}
</script>

2、修改State

1、单个参数修改 state

store.counter++

2、多个参数修改 state

store.$patch({
  counter: store.counter + 1,
  name: 'Abalam',
})

3、全部修改 state

store.$state = { counter: 666, name: 'Paimon' }
或
pinia.state.value = {}

3、重置State

将状态重置为初始值

const store = useStore()
store.$reset()

4、vue2写法

import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'

export default {
  computed: {
    ...mapState(useCounterStore, ['counter'])
    // 或
    ...mapState(useCounterStore, {
      myOwnName: 'counter',
      double: store => store.counter * 2,
      magicValue(store) {
        return store.someGetter + this.counter + this.double
      },
    }),
  },
}

四、Getters

getter 中的值有缓存特性,类似于computed,如果值没有改变,多次使用也只会调用一次

1、定义Getters:

export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
  }),
  getters: {
    doubleCount: (state) => state.counter * 2,
    // 自动推导返回类型
    doubleCount(state) {
      return state.counter * 2
    },
    // 依赖getters返回参数,则需要显性的设置返回类型
    doublePlusOne(): number {
      return this.doubleCount + 1
    },
  },
})

2、使用Getters

<template>
  <p>Double count is {{ store.doubleCount }}</p>
</template>

<script>
export default {
  setup() {
    const store = useStore()
    
    return { store }
  },
}
</script>

3、vue2写法

import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'

export default {
  computed: {
    ...mapState(useCounterStore, ['doubleCount'])
    // 或
    ...mapState(useCounterStore, {
      myOwnName: 'doubleCounter',
      double: store => store.doubleCount,
    }),
  },
}

五、Actions

Pinia 中删除了 MutationActions 支持同步和异步

1、定义 Actions

// 同步
export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
    userData: null,
  }),
  actions: { 
    increment() {
      this.counter++
    },
    randomizeCounter() {
      this.counter = Math.round(100 * Math.random())
    },
  },
})
// 异步
import { mande } from 'mande'
const api = mande('/api/users')

export const useUsers = defineStore('users', {
  state: () => ({
    userData: null,
  }),

  actions: {
    async registerUser(login, password) {
      this.userData = await api.post({ login, password })
    },
  },
})

2、调用 Actions

1、可以直接调用 store 的任何方法

<script>
export default {
  setup() {
    const store = useStore()
    store.randomizeCounter()
  },
}
</script>

2、action 间的相互调用,直接用 this 访问即可。

 export const useUserStore = defineStore({'user',
  actions: {
    increment() {
      this.counter++
    },
    increase() {
      // 调用另一个 action 的方法
      this.increment()
    },
  }
})

3、在 action 里调用其他 store 里的 action 也比较简单,引入对应的 store 后即可访问其内部的方法。

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

export const useSettingsStore = defineStore('settings', {
  state: () => ({
    preferences: null,
  }),
  actions: {
    async fetchUserPreferences() {
      // 调用 auth-store store 里的 action 方法
      const auth = useAuthStore()
      if (auth.isAuthenticated) {
        this.preferences = await fetchPreferences()
      } else {
        throw new Error('User must be authenticated')
      }
    },
  },
})

3、vue2写法

import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counterStore'

export default {
  methods: {
    ...mapActions(useCounterStore, ['increment'])
    // 或
    ...mapActions(useCounterStore, { myOwnName: 'doubleCounter' }),
  },
}

总结

总得来说,Pinia 就是 Vuex 的官方替代版,可以更好的支持 Vue2,Vue3以及TypeScript。在 Vuex 的基础上去掉了 Mutation模块嵌套等概念,语法更简洁直接, 更符合 Vue3 的 Composition api,为 TypeScript 提供了更好的类型推导。以上是 Pinia.js 用法的一些介绍,Pinia.js 的内容还远不止这些,更多内容及使用有待大家自己探索。Pinia文档

参考文章:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容