持续创作,加速成长!这是我参与「掘金日新计划 · 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')
使用
这里我们如图所示创建如下目录
基础用法
我们先讲下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'))
在控制台打印,得到如图所示结果
接下来我们再详细说下关于pinia的每个模块的一些特性方法
state
这里咱们主要说下修改state
状态的几种方法
- 直接修改
const store = useStore()
store.counter++
- 通过$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
})
- 通过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太多了吧。
最后 本篇完结!撒花! 感谢观看! 希望能帮助到你!