vue3中pinia的使用

9 篇文章 0 订阅

1. 安装

npm i pinia -S

2. 项目中引入和使用

main.ts

// vue3 版本api
import { createPinia } from 'pinia'

// 返回一个vue插件
const store = createPinia()

// 使用pinia插件
app.use(store)

3. 创建store

  • store 是一个用reactive 包裹的对象
  • 可以根据需要定义任意数量的 store ,我们以一个userStore为例
  • pinia怎么知道有多少个store呢?(答案是,只要定义的store执行了defineStore返回的函数,pinia就认为这个store激活了)
import { defineStore } from 'pinia'

// defineStore('user', ...) 和下面的形式相同
export const useUserStore = defineStore({
  id: 'user',
  state: () => ({
    userid: localStorage.getItem('userid') || '',
  }),
  actions: {
    setUserId(payload: string) {
      this.userid = payload
    }
  }
})

4. state

4.1 获取state

<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { computed } from 'vue'
import { storeToRefs } from 'pinia'
// 方式1,计算属性方式
const userid = computed(() => useUserStore().userid)
// 方式2, 通过user.userid的方式使用
const user = useUserStore()
// 方式3, 使用toRef获取userid
const userid = toRef(useUserStore(), 'userid')
// 方式4, 借助pinia提供的api: storeToRefs 实现
const { userid } = storeToRefs(user)
</script>

4.2 修改state

<script setup lang="ts">
import { useUserStore } from '@/stores/user'

const user = useUserStore()
// 方式1: 直接修改,vuex不允许这种方式(需要提交mutation),但pinia是允许的
user.userid = 'xxx'
  // 方式2: 
  user.$patch({userid: 'xxx'})
  // 方式3: 
  user.$patch((state) => { state.userid = 'xxx' })
  // 方式4:
  user.$state = { userid:'xxx' }
  // 方式5: 使用actions
  user.setUserId('xxx')
</script>

5. actions

  • actions包含同步actions和异步actions
const login = (): Promise<string> => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('userid')
    }, 2000);
  })
}
// defineStore('user', ...) 和下面的形式相同
export const useUserStore = defineStore({
  id: 'user',
  state: () => ({
    userid: localStorage.getItem('userid') || '',
  }),
  actions: {
    // 同步actions
    setUserId(payload: string) {
      console.log(payload)
      this.userid = payload
    },
    // 异步actions
    async getUser() {
      // actions可以互相调用
      this.setUserId(await login())
    }
  }
})
  • actions可以互相调用,我们把actions调用关系互换一下
actions: {
  // 在同步actions中调用异步actions
  setUserId(payload: string) {
    console.log(payload)
    this.getUser()
  },
    async getUser() {
      // this.setUserId(await login())
      this.userid = await login()
    }
}

6. getters

  • 相当于组件中的computed也具有缓存

  • 接收"状态"作为第一个参数

  • state: () => ({
      userid: localStorage.getItem('userid') || '',
      counter: 0
    }),
    getters: {
      doubleCount: (state) => state.counter * 2,
    },
    

6.1 常规函数使用this的注意事项

  • 定义常规函数时可以通过 this 访问到 整个 store 的实例, 但是需要定义返回类型(在 TypeScript 中)。 这是由于 TypeScript 中的一个已知限制,并且不会影响使用箭头函数定义的 getter,也不会影响不使用 this 的 getter

  • export const useStore = defineStore('main', {
      state: () => ({
        counter: 0,
      }),
      getters: {
        // 自动将返回类型推断为数字
        doubleCount(state) {
          return state.counter * 2
        },
        // 返回类型必须明确设置
        doublePlusOne(): number {
          // 调用其他getter: 
          return this.doubleCount + 1
          // 等同于:
          return this.counter * 2 + 1
        },
      },
    })
    

6.2 接收参数传递

  • Getters 只是幕后的 computed 属性,因此无法向它们传递任何参数。 但是,您可以从 getter 返回一个函数以接受任何参数:

  • 这种操作getter不再缓存,只相当于在调用函数(从store 的解构中可以看出)

  • export const useStore = defineStore('main', {
      getters: {
        getUserById: (state) => {
          return (userId) => state.users.find((user) => user.id === userId)
        },
      },
    })
    
  • 组件

  • // store
    getters: {
      doubleCount: (state) => state.counter * 2,
      doublePlusOne(): number {
        // 等同于调用其他getter: return this.doubleCount + 1
        return this.counter * 2 + 1
      },
      payloadCount() {
        return (payload: number) => this.doublePlusOne + payload
      }
    },
    
    // 组件
    const { userid, payloadCount, } = storeToRefs(user)
    
  • 模板

  • <main> home: {{ user.userid }} {{ doubleCount }} {{ payloadCount(3) }} </main>
    

7. store apis

  • $reset 重置状态

  • store.$reset()
    
  • $patch 派发actions

  • // 方式1: 
    user.$patch({userid: 'xxx'})
    // 方式2: 
    cartStore.$patch((state) => {
      state.items.push({ name: 'shoes', quantity: 1 })
      state.hasChanged = true
    })
    
  • $state 访问 store 状态

  • store.$state = { counter: 666, name: 'Paimon' }
    
  • $subscribe, 订阅变更

    • 优点:即使触发了某个action,但newval 和 oldval相等也不会触发这个订阅
  • cartStore.$subscribe((mutation, state) => {
      // import { MutationType } from 'pinia'
      mutation.type // 'direct' | 'patch object' | 'patch function'
      // 与 cartStore.$id 相同
      mutation.storeId // 'cart'
      // 仅适用于 mutation.type === 'patch object'
      mutation.payload // 补丁对象传递给 to cartStore.$patch()
    
      // 每当它发生变化时,将整个状态持久化到本地存储
      localStorage.setItem('cart', JSON.stringify(state))
    })
    
    // 可以利用该api针对某些 mutation.events.key 结合 localStorage 做判断实现数据持久化
    user.$subscribe((mutation, state) => {
      /**
       * 下面是 mutation的属性
       * key: "counter"
        newValue: 1
        oldValue: 0
        target: {userid: 'userid', counter: 1}
        type: "set"
        oldTarget: undefined
       */
      console.log('$%#', mutation.events as any, state)
    })
    

    $onAction, 监听actions触发

    可以利用after做一些事情

    context.store.$onAction((arg) => {
      	// arg 属性
      	/**
         * after: ƒ after(callback)
          args: [] actions 参数
          name: "setUserId"
          onError: ƒ onError(callback)
          store: action所在实例
         * 
         */
        if(arg.name === '"setUserId"') {
          console.log('onaction', arg)
          arg.after(() =>{
            // do something
          })
        }
      })
    

8. plugins

  • pinia 插件是一个函数,可以选择返回要添加到store的属性,
  • 该函数可以接收一个可选参数: context (ts类型:PiniaPluginContext)
    • context.pinia // 使用 createPinia() 创建的 pinia
    • context.app // 使用 createApp() 创建的当前应用程序(仅限 Vue 3)
    • context.store // 插件正在扩充的 store
    • context.options // 定义存储的选项对象传递给defineStore()
  • 只有store调用了导出的useXXX函数,插件函数才会运行并对该store进行处理
import type { PiniaPluginContext } from 'pinia'

// 持久化插件
export function persistencePlugin(context: PiniaPluginContext) {
  /**
     * key: "counter"
      newValue: 1
      oldValue: 0
      target: {userid: 'userid', counter: 1}
      type: "set"
      oldTarget: undefined
      */
  context.store.$subscribe((mutation: any) => {
    if (mutation.storeId === 'user') {
      if (mutation.events.key === 'userid') {
        localStorage.setItem('userid', mutation.events.newValue)
      }
    }
  })

  /**
     * after: ƒ after(callback)
      args: [] actions 参数
      name: "setUserId"
      onError: ƒ onError(callback)
      store: action所在实例
     * 
     */
  context.store.$onAction((arg) => {
    if (arg.name === '"setUserId"') {
      console.log('onaction', arg)
    }
  })
}

9. mock数据

1. npm i mockjs vite-plugin-mock

2.在vite.config.ts中加入配置
import { viteMockServe } from "vite-plugin-mock";

plugins: [
    viteMockServe({
      supportTs: true,
      logger: false,
      mockPath: "./mock/",
    }),
  ],
    
3. 在根文件同等路径下去建立一个文件夹,取名mock.ts

4. 然后写入想要请求的接口的数据
export default [
  {
    url: "/api/banner/list",
    method: "get",
    response: () => {
      return {
        code: "200",
        message: "获取成功",
        data: [
          {
            img: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimages.uiiiuiii.com%2Fwp-content%2Fuploads%2F2019%2F07%2Fi-bn20190630-1-15.jpg&refer=http%3A%2F%2Fimages.uiiiuiii.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671816683&t=35a1fc5f8bd1483aa69fbd16dc7e7524",
            alt: "112",
            link: "112",
            bannerid: "banner_6a1f7001-e580-4d8b-b5dd-7447f94c1990",
            flag: true,
          },
          {
            img: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01f6ad5dde2b7ca80120686b55f619.jpg%3Fx-oss-process%3Dimage%2Fauto-orient%2C1%2Fresize%2Cm_lfit%2Cw_1280%2Climit_1%2Fsharpen%2C100&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671816683&t=a4402cf070e22b867a74cc0d23a8c4e7",
            alt: "1123",
            link: "1123",
            bannerid: "banner_35f7aa34-6745-4f88-934b-7b506330a695",
            flag: true,
          },
          {
            img: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F013aa3594fac92a8012193a3cabdb4.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671816683&t=0009e5acc265a854d79350e84b9cbb2a",
            alt: "11234",
            link: "11234",
            bannerid: "banner_52cd31c3-7811-461c-9f1a-239845ab0578",
            flag: true,
          },
          {
            img: "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F0183245697b4f66ac725af23df2dcc.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1671816683&t=c60c7fb9f4d4935208407b8c8e874c74",
            alt: "112345",
            link: "112345",
            bannerid: "banner_1ba6cee8-d2de-47c2-955b-cd5c773f6d70",
            flag: true,
          },
        ],
      };
    },
  },
];


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

goto_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值