为什么要使用 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

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 返回一个函数以接受任何参数

在组件中使用

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)

// ...

},

}

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: