两分钟上手 pinia

安装

官网

yarn add pinia
# or with npm
npm install pinia

初始化 Pinia

import { createPinia } from 'pinia'

const pinia = createPinia()
pinia.use(SomePiniaPlugin) // 给 pinia 装插件

const app = createApp(App)
app.use(pinia)

这里需要注意时间顺序:只有在调用 app.use(pinia) 之后才能调用 useXxxStore()

使用 Store

注意

  1. defineStore 接受一个 id,不同数据源的 id 必须是不同的
  2. 不能将 useCounter() 的返回值解构,这会导致数据响应式的丢失

写法一:

更像原先的 vuex

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

export const useCounterStore = defineStore('counterStore', {
  state: ()=> {
    return {j: 0, k: 0}
  }
})

// Counter.vue
import { useCounterStore } from 'path/to/src/stores/counterStore'

export default {
  setup() {
    const counterStore = useCounterStore()
    // TODO 默认情况下可以直接这么更改,但是不推荐 // https://pinia.vuejs.org/core-concepts/state.html#accessing-the-state
    counterStore.j ++
    
    // 这里在视图里使用 counterStore.j 和 counterStore.k
    // 但你不能解构 counterStore,只能像下面这样解构:
    const { j, k } = storeToRefs(counterStore) // 注意:这里会自动忽略 方法 和 非响应式数据(Creates an object of references with all the state, getters, and plugin-added state properties of the store. Similar to toRefs() but specifically designed for Pinia stores so methods and non reactive properties are completely ignored.)
    return {
      counterStore, j, k,
    }
  },
}
Store Getters

getters 其实就是 store 的计算属性集合,而且 getter 不能是异步函数

export const useStore = defineStore('main', {
  state: () => ({
    counter: 0,
  }),
  getters: {
    doubleCount(state) {
      return state.counter * 2
    },
    doublePlusOne() {
      return this.doubleCount + 1 // getter 访问另一个 getter 或者 state 可以用 this
    },
    getUserById: (state) => { // getter 可以返回一个函数,不过这会导致缓存失效
      return (userId) => state.users.find((user) => user.id === userId)
    },
    otherGetter(state) { // 你还可以调用其他的 store
      const otherStore = useOtherStore()
      return state.localData + otherStore.data
    },
  },
})
// store.doubleCount 和 store.doublePlusOne 就可以直接当做属性使用了
// store.getUserById(userId) 可以当做函数使用
Store Actions

action 其实就是 store 的 methods,而且可以是异步函数

export const useUserStore = defineStore('users', {
  state: () => ({
    userData: null,
  }),
  actions: {
    async getUser(token) {
      this.userData = await api.post({ token })
    },
  },
})
// 然后你就可以使用 userStore.getUser(token) 了

写法二:

推荐这种,符合Vue3 setup的编程模式,让结构更加扁平化

import { ref, computed } from 'vue';
import { defineStore } from 'pinia';

export const useUserStore = defineStore('users', () => {
  const userData= ref({});
  const getUser = async () => {
    userData.value = await api.post({ token })
  }
  
  const userName = computed(() => userData.value.name)

  return { userData, userName, getUser };
});

store.$patch(object | fn)

批量更新

counterStore.$patch(
   { name: 'pinia', age: counterStore.age + 1 } 
)

cartStore.$patch((state) => {
  state.items.push({ name: 'vuex', age: 18 })
  state.hasChanged = true
})

store.$subscribe(fn)

用于监听 state 的整体变化。

cartStore.$subscribe((mutation, state) => {
  // import { MutationType } from 'pinia'
  mutation.type // 'direct' | 'patch object' | 'patch function'
  mutation.storeId  
  mutation.payload // 获取 $patch 接收到的参数

  localStorage.setItem('cart', JSON.stringify(state))
})

它有一个很方便的特性是会自动在组件卸载时注销,如果你不想要,可以在 $subscribe 第二个参数处传入 {detached: true} 选项。

你也可以使用 watch 达到类似的效果:

watch(
  pinia.state,
  (state) => {
    localStorage.setItem('piniaState', JSON.stringify(state))
  },
  { deep: true }
)

store.$onAction()

用于监控所有 action 的执行情况。

const unsubscribe = someStore.$onAction(
  ({
    name, // action 的名字
    store, // store === someStore
    args, // action 的实际参数
    after, // action 成功之后执行 after
    onError, // action 失败之后执行 onError
  }) => {
    const startTime = Date.now()
    console.log(`开始执行 "${name}" 参数为 [${args.join(', ')}].`)
    after((result) => {
      console.log(
        `执行成功 "${name}" 用时 ${Date.now() - startTime}毫秒\n结果为:${result}`
      )
    })
    onError((error) => {
      console.warn(
        `执行失败 "${name}" 用时 ${Date.now() - startTime}毫秒\n报错为:${error}.`
      )
    })
  }
)
// $onAction 会在它所在组件卸载时自动销毁
// 如果你将 $onAction 的第二个参数设置为 true,那么你需要自己调用 unsubscribe 来取消监听。

store.$reset()

你可以使用 counterStore.$reset() 重置 state

store.$state

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

推荐阅读更多精彩内容