为什么要使用 Pinia?
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。
Pinia的好处:
1.dev-tools支持
跟踪动作,突变的时间线
Store出现在使用他们的组件中
time travel 和 更容易的调试
2.热模块更换
在不重新加载页面的情况下修改您的 Store
在开发时保持任何现有状态
3.插件
使用插件扩展 Pinia 功能
4.为 JS 用户提供适当的 TypeScript 支持或 autocompletion
5.服务器端渲染支持
与Vuex的比较
与Vuex相比,pinia提供了更简单的API,更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
mutations 不再存在。他们经常被认为是 非常 冗长。他们最初带来了 devtools 集成,但这不再是问题。
与 Vuex 3.x/4.x 的比较
mutations 不再存在。他们经常被认为是 非常 冗长。他们最初带来了 devtools 集成,但这不再是问题。
不再需要注入、导入函数、调用函数
安装Pinia
安装:npm i pinia
创建pinia(根存储)并且全局都能使用
import { createPinia } from 'pinia'
app.use(createPinia())
什么是 Store
什么是Store
简单来说就是 托管全局状态 一个任何人都能读取和写入的组件
它有三个概念state、getters 和 actions 等同于组件中的 数据、计算和方法
定义一个Store
Store 是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递
import { defineStore } from 'pinia'
// useStore 可以是 useUser、useCart 之类的任何东西
// 第一个参数是应用程序中 store 的唯一 id
export const useStore = defineStore('main', {
// other options...
})
//这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools。 将返回的函数命名为 use... 是跨可组合项的约定
定义 一个 store,因为在 setup() 中调用 useStore() 之前不会创建 store
import { useStore } from '@/stores/counter'
export default {
setup() {
const store = useStore()
return {
// 可以返回整个 store 实例以在模板中使用它
store,
}
},
}
//可以根据需要定义任意数量的 store 在不同的文件中定义每个 store 充分利用pinia
//store 被实例化 可以直接在 store 上访问 state、getters 和 actions 中定义的任何属性
3.从 Store 中提取属性同时保持其响应式,需要使用storeToRefs()。 它为任何响应式属性创建 refs。 当使用 store 中的状态但不调用任何操作
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useStore()
// `name` 和 `doubleCount` 是响应式引用
// 这也会为插件添加的属性创建引用
// 但跳过任何 action 或 非响应式(不是 ref/reactive)的属性
const { name, doubleCount } = storeToRefs(store)
return {
name,
doubleCount
}
},
})
State
state 是 store 的核心部分
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// 推荐使用 完整类型推断的箭头函数
state: () => {
return {
// 所有这些属性都将自动推断其类型
counter: 0,
name: 'Eduardo',
isAdmin: true,
}
},
})
2.访问state
const store = useStore()
store.counter++
//通过 store 实例访问状态来直接读取和写入状态
3.重置状态
const store = useStore()
store.$reset()
//通过调用 store 上的 $reset() 方法将状态 重置 到其初始值
4.使用选项API
// Example File Path:
// ./src/stores/counterStore.js
import { defineStore } from 'pinia',
const useCounterStore = defineStore('counterStore', {
state: () => ({
counter: 0
})
})
5.使用 setup( )
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
computed: {
tripleCounter() {
return counterStore.counter * 3
},
},
}
//setup() 钩子可以使在 Options API 中使用 Pinia 不需要额外的 map helper
6.不使用 setuo( )
import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// 允许访问组件内部的 this.counter
// 与从 store.counter 读取相同
...mapState(useCounterStore, {
myOwnName: 'counter',
// 您还可以编写一个访问 store 的函数
double: store => store.counter * 2,
// 它可以正常读取“this”,但无法正常写入...
magicValue(store) {
return store.someGetter + this.counter + this.double
},
}),
},
}
//不使用 Composition API,并且使用的是 computed、methods、...,则可以使用 mapState() 帮助器将状态属性映射为只读计算属性
7.可修改状态
import { mapWritableState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// 允许访问组件内的 this.counter 并允许设置它
// this.counter++
// 与从 store.counter 读取相同
...mapWritableState(useCounterStore, ['counter'])
// 与上面相同,但将其注册为 this.myOwnName
...mapWritableState(useCounterStore, {
myOwnName: 'counter',
}),
},
}
//想写入一些状态属性,可以使用 mapWritableState() 代替
8.替换state
store.$state = { counter: 666, name: 'Paimon' }
//通过将其 $state 属性设置为新对象来替换 Store 的整个状态
Getters
Getter 完全等同于 Store 状态的计算值
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
doubleCount: (state) => state.counter * 2,
},
})
//可以用 defineStore() 中的 getters 属性定义 接收“状态”作为第一个参数以鼓励箭头函数的使用
2.直接在 store 实例上访问 getter
Double count is {{ store.doubleCount }}
export default {
setup() {
const store = useStore()
return { store }
},
}
3.访问其他 getter
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
getters: {
// 类型是自动推断的,因为我们没有使用 `this`
doubleCount: (state) => state.counter * 2,
// 这里需要我们自己添加类型(在 JS 中使用 JSDoc)。 我们还可以
// 使用它来记录 getter
/**
* 返回计数器值乘以二加一。
*
* @returns {number}
*/
doubleCountPlusOne() {
// 自动完成 ✨
return this.doubleCount + 1
},
},
})
//与计算属性一样,可以组合多个 getter。 通过 this 访问任何其他 getter
4.将参数传递给 getter
export const useStore = defineStore('main', {
getters: {
getUserById: (state) => {
return (userId) => state.users.find((user) => user.id === userId)
},
},
})
//Getters 只是幕后的 computed 属性,因此无法向它们传递任何参数。 可以从 getter 返回一个函数以接受任何参数
在组件中使用
export default {
setup() {
const store = useStore()
return { getUserById: store.getUserById }
},
}
User 2: {{ getUserById(2) }}
5.访问其他Store的getter
import { useOtherStore } from './other-store'
export const useStore = defineStore('main', {
state: () => ({
// ...
}),
getters: {
otherGetter(state) {
const otherStore = useOtherStore()
return state.localData + otherStore.data
},
},
})
//要使用其他存储 getter,可以直接在 better 内部使用它
6.与 setup( )一起使用
export default {
setup() {
const store = useStore()
store.counter = 3
store.doubleCount // 6
},
}
//可以直接访问任何 getter 作为 store 的属性(与 state 属性完全一样)
7.使用选项API
使用 setup( )
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
computed: {
quadrupleCounter() {
return counterStore.doubleCounter * 2
},
},
}
没有 setup( )
import { mapState } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
computed: {
// 允许访问组件内的 this.doubleCounter
// 与从 store.doubleCounter 中读取相同
...mapState(useCounterStore, ['doubleCount'])
// 与上面相同,但将其注册为 this.myOwnName
...mapState(useCounterStore, {
myOwnName: 'doubleCounter',
// 您还可以编写一个访问 store 的函数
double: store => store.doubleCount,
}),
},
}
//可以使用 previous section of state 中使用的相同 mapState() 函数映射到 getter
Actions
1.Actions 相当于组件中的 methods
export const useStore = defineStore('main', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++
},
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})
//可以使用 defineStore() 中的 actions 属性定义
2.actions 可以是异步的,您可以在其中await 任何 API 调用甚至其他操作
import { mande } from 'mande'
const api = mande('/api/users')
export const useUsers = defineStore('users', {
state: () => ({
userData: null,
// ...
}),
actions: {
async registerUser(login, password) {
try {
this.userData = await api.post({ login, password })
showTooltip(`Welcome back ${this.userData.name}!`)
} catch (error) {
showTooltip(error)
// 让表单组件显示错误
return error
}
},
},
})
3.自由地设置你想要的任何参数并返回任何东西
export default defineComponent({
setup() {
const main = useMainStore()
// Actions 像 methods 一样被调用:
main.randomizeCounter()
return {}
},
})
//调用 Action 时,一切都会自动推断
4.访问其他 store操作
要使用另一个 store ,可以直接在操作内部使用它
import { useAuthStore } from './auth-store'
export const useSettingsStore = defineStore('settings', {
state: () => ({
// ...
}),
actions: {
async fetchUserPreferences(preferences) {
const auth = useAuthStore()
if (auth.isAuthenticated) {
this.preferences = await fetchPreferences()
} else {
throw new Error('User must be authenticated')
}
},
},
})
5.与 setup( )一起使用
您可以直接调用任何操作作为 store 的方法
export default {
setup() {
const store = useStore()
store.randomizeCounter()
},
}
6.使用选项API
import { defineStore } from 'pinia',
const useCounterStore = defineStore('counterStore', {
state: () => ({
counter: 0
}),
actions: {
increment() {
this.counter++
}
}
})
使用setup()
import { useCounterStore } from '../stores/counterStore'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
methods: {
incrementAndPrint() {
counterStore.increment()
console.log('New Count:', counterStore.count)
},
},
}
不使用 setup()
import { mapActions } from 'pinia'
import { useCounterStore } from '../stores/counterStore'
export default {
methods: {
// gives access to this.increment() inside the component
// same as calling from store.increment()
...mapActions(useCounterStore, ['increment'])
// same as above but registers it as this.myOwnName()
...mapActions(useCounterStore, { myOwnName: 'doubleCounter' }),
},
}
7.订阅 Actions
可以使用 store.$onAction() 订阅 action 及其结果。 传递给它的回调在 action 之前执行。 after 处理 Promise 并允许您在 action 完成后执行函数。
const unsubscribe = someStore.$onAction(
({
name, // action 的名字
store, // store 实例
args, // 调用这个 action 的参数
after, // 在这个 action 执行完毕之后,执行这个函数
onError, // 在这个 action 抛出异常的时候,执行这个函数
}) => {
// 记录开始的时间变量
const startTime = Date.now()
// 这将在 `store` 上的操作执行之前触发
console.log(`Start "${name}" with params [${args.join(', ')}].`)
// 如果 action 成功并且完全运行后,after 将触发。
// 它将等待任何返回的 promise
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
// 如果 action 抛出或返回 Promise.reject ,onError 将触发
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
// 手动移除订阅
unsubscribe()
action subscriptions 绑定到添加它们的组件(如果 store 位于组件的 setup() 内)。当组件被卸载时,它们将被自动删除。 如果要在卸载组件后保留它们,将 true 作为第二个参数传递给当前组件的 detach action subscription
export default {
setup() {
const someStore = useSomeStore()
// 此订阅将在组件卸载后保留
someStore.$onAction(callback, true)
// ...
},
}
推荐文章
发表评论