大人!时代变了!让我们拥抱大菠萝--Pinia

138 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

一、前言

本篇主要讲述vue3项目如何使用封装pinia来替代vuex,并讲述一下pinia的使用方法。本文所使用的环境为:vue3 + vite3。点击查看Pinia中文文档

二、与vuex的区别

与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。

在我的使用经历中,最重要的不同就是pinia没有mutations,并且pinia中的状态值,我们可以通过多种方式修改,而不是像vuex一样只能通过mutation去修改state中的状态值,这点对我来说是最大的变化。关于更多的区别,大家可以去看下官方文档,点击查看差异详情

三、引入和封装Pinia

安装

npm i pinia
// or
yarn add pinia

引入

// main.ts
import { createPinia } from 'pinia'

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

使用

这里我们如图所示创建如下目录

image.png

基础用法

我们先讲下user.ts中的内容,这里我们需要知道 Store 是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递,代码如下

import { defineStore } from 'pinia'
// 定义用户类型
interface IUser {
  name: string
  id: string
}

export default defineStore('user', {
  state() {
    return {
      counter: 1 as number,
      userList: [] as IUser[]
    }
  },
  getters: {
    doubleCount: (state) => state.counter * 2,
    // 返回类型必须明确设置,这里使用this,也可以获取其他模块的数据
    doublePlusOne(): number {
      return this.counter * 2 + 1
    },
    // 通过传递的userId来判断获取那个人员信息
    getUserById: (state) => {
      return (userId:string) => state.userList.find((user) => user.id === userId)
    }
  },
  actions: {
    getList() {
      // 模拟获取数据
      let resList: IUser[] = [
        { name: "李雷", id: '1' },
        { name: "韩梅梅", id: '2' }
      ];
      this.userList = resList;
    },
  },
})

在上一步定义好user相关的store之后,我们就可以在页面中引入并使用了,如下所示

// 引入定义的文件
import useUserStore from '../../store/modules/user'
const store = useUserStore()
console.log('state===', store.counter)
console.log('state===初始值', store.userList)
// 使用action中的方法
store.getList()
console.log('state===获取后的值', store.userList)
console.log('getter===', store.getUserById('1'))

在控制台打印,得到如图所示结果

image.png

接下来我们再详细说下关于pinia的每个模块的一些特性方法

state

这里咱们主要说下修改state状态的几种方法

  1. 直接修改
const store = useStore()

store.counter++
  1. 通过$patch来修改 这里推荐使用第二种方式
const store = useStore()
// 方式一
store.$patch({
  counter: store.counter + 1,
  userList: [...store.userList, { id: '2' , name: '大壮'}]
})

// 方式二
const store = useStore()
store.$patch((state) => {
  state.userList.push({ id: '2' , name: '大壮' })
  state.counter = 20
})
  1. 通过actions来修改
// 使用action中的方法
const store = useStore()

store.getList()

getters

这里咱们我说下getters的几种定义方式,getters就类似于vue中的计算属性computed。并且通过 this 访问任何其他 getter

export const useStore = defineStore('main', {
  getters: {
    // 1.基础用法
    doubleCount: (state) => state.counter * 2,
    // 2.返回类型必须明确设置,这里使用this,也可以获取其他模块的数据
    doublePlusOne(): number {
      return this.counter * 2 + 1
    },
    // 3.通过传递的userId来判断获取那个人员信息
    getUserById: (state) => {
      return (userId:string) => state.userList.find((user) => user.id === userId)
    }
  },
})

actions

这里action是支持同步和异步的。就类似于vue中的methods一样。

封装

这个是在使用过程中,发现如果一个页面需要使用pinia中的方法或者状态,都需要写这两行代码

import useUserStore from '../../store/modules/user'
const store = useUserStore()

即引入文件,并在本页面定义。然后我就在想如何把这些不同的store做一个统一的出口,并把这些store引入到全局。

我是这么做的,通过vite的import.meta.glob方法,将所有的store内容获取到,然后在将他们挂载到window上面,代码如下所示

// store/index.ts

// {eager: true} 这个参数是为了同步获取
const modules = import.meta.glob('./modules/*.ts', { eager: true })
let storeData:any = {}
for (const path in modules) {
  const key:string = path.replace(/(.*\/)*([^.]+).ts/ig,"$2")
  const mod:any = modules[path]
  storeData[key] = mod.default
}
export default function useStore () {
  return Object.keys(storeData).reduce((pre:any, cue:string) => {
    pre[cue] = storeData[cue]()
    return pre
  }, {})
}

挂载到window上

import useStore from './store'
// 若ts需要全局定义一下
window.$useStore = useStore()

之后我们在使用store的时候就可以直接这么用了

  // user对应的就是模块文件名字
  const { user } = window.$useStore
  console.log('state===', user.userList)

这样我们就完成了封装。

三、后记

第一:封装虽然完成了,但是使用过程中出现了这么个问题,就是我定义的每个状态值的类型都变成any了,ORZ...

我ts用的少,欢迎指正。

第二:vue3目前在推行hooks,封装后省略了一步引入可能并没有必要,但我觉得这么使用方便...可能我用vue2太多了吧。

最后 本篇完结!撒花! 感谢观看! 希望能帮助到你!