什么是Pinia?
- Pinia本质上依然是一个
状态管理的库
,它允许你跨组件/页面
进行状态共享。
Pinia和Vuex的区别?
- 与Vuex相比,Pinia提供了一个
更简单的API
,具有更少的规范
,提供了Composition-API风格的API
。 - 最重要的是,在与
TypeScript
一起使用时具有可靠的类型推断支持
。
和Vuex相比,Pinia有什么优势?
- mutations不再存在,只有state,gettes,actions。
- 更友好的TypeScript支持。
- 不再有modules的嵌套结构,每个store都是独立的,互不影响。
- 没有命名空间模块。
- 无需动态添加 Store,默认情况下它们都是动态的。
- 不再需要注入、导入函数、调用函数。
- 支持插件来扩展自身功能。
- 支持服务端渲染(SSR)。
上面列出的只是Pinia的一些主要的优点,还有其它的优点,需要大家在使用的时候慢慢去体会,下面一起来看看Pinia是如何使用的。
如何使用Pinia?
安装Pinia
yarn add pinia
或者
npm install pinia
创建Pinia
// src/stores/index.js
import { createPinia } from "pinia"
const pinia = createPinia ()
export default pinia
挂载Pinia
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
const app = createApp(App)
app.use(pinia)
app.mount('#app')
定义一个Store
- 我们需要知道 Store 是使用
defineStore()
定义的。 - 并且它需要一个
唯一名称
,作为第一个参数传递。 - 这个name,也称为id,是必需的,Pinia使用它来将store连接到devtools。
- 返回的函数统一使用
useXXX
作为命名方案,这是约定的一种规范。
// src/stores/counter.js
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1
})
})
export default useCounter
使用Store
- store在它被使用之前是不会被创建的,我们可以通过use函数来使用store。
// src/views/Home.vue
<script setup>
import useCounter from '@/stores/counter'
const counterStore = useCounter()
</script>
<template>
<div class="home">
<h2>count: {{ counterStore.count }}</h2>
</div>
</template>
解构Store
注意:
- 使用传统的方式将属性从store中解构出来,虽然可以获取到值,但是不具有响应式。
// src/views/Home.vue
<script setup>
import useCounter from '@/stores/counter'
const counterStore = useCounter()
// 将counter从store中解构出来,此时我们再去修改count,count的值可以修改,但是页面的值不会发生变化,不具有响应式
const { count } = counterStore
cosnt incrementCount = () => {
counterStore.count++
}
</script>
<template>
<div class="home">
<!-- count值不会随着counter++响应式更新 -->
<h2>count: {{ counterStore.count }}</h2>
<button @click="incrementCount">count++</button>
</div>
</template>
- 为了从store中解构属性的时候保持其响应式,我们可以使用pinia提供的
storeToRefs
函数。
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
// 使用storeToRefs解构
const { count } = storeToRefs(counterStore)
cosnt incrementCount = () => {
counterStore.count++
}
</script>
<template>
<div class="home">
<!-- 使用storeToRefs解构 -->
<h2>count: {{ count }}</h2>
<button @click="incrementCount">count++</button>
</div>
</template>
- 或者使用vue提供的
toRefs
函数。
// src/views/Home.vue
<script setup>
import { toRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
// 使用toRefs 解构
const { count } = toRefs(counterStore)
cosnt incrementCount = () => {
counterStore.count++
}
</script>
<template>
<div class="home">
<!-- 使用toRefs 解构 -->
<h2>count: {{ count }}</h2>
<button @click="incrementCount">count++</button>
</div>
</template>
Pinia中操作state
- 默认情况下,可以通过
store.属性名
来修改state。 - 可以通过调用store上的
$reset()
方法将state重置到其初始值。 - 如果想要一次修改多个state,可以调用store上的
$patch
方法。
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useUser from '@/stores/user'
const userStore = useUser()
const { name, age, height } = storeToRefs(userStore)
// 修改state,一个个修改
cosnt changeState = () => {
userStore.name = "jackson"
userStore.age = 25
userStore.height = 1.8
}
// 重置state
const resetState = () => {
userStore.$reset()
}
// 修改state,一次修改多个
const changeMoreState = () => {
userStore.$patch({
name: "jackson",
age: 25,
height: 1.8
})
}
</script>
<template>
<div class="home">
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
<h2>height: {{ height }}</h2>
<button @click="changeState">修改state</button>
<button @click="resetState">重置state</button>
<button @click="changeMoreState">修改多个state</button>
</div>
</template>
// sre/stores/user.js
import { defineStore } from 'pinia'
const useUser = defineStore("user", {
state: () => ({
name: "james",
age: 18,
height: 1.7
})
})
export default useUser
Pinia中的getters
定义和访问getter
- getters相当于store的计算属性。
- getters中可以接收一个state作为参数的函数。
// sre/stores/counter.js
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1,
firstName: "Jackson",
lastName: "vision",
}),
getters: {
doubleCount: (state) => state.count * 2,
fullName: (state) => `${state.firstName} ${state.lastName}`,
}
})
export default useCounter
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
</script>
<template>
<div class="home">
<h2>doubleCount: {{ counterStore.doubleCount }}</h2>
<h2>fullName: {{ counterStore.fullName}}</h2>
</div>
</template>
- 在一个getters方法中也可以调用其它的getters方法。下图中,我们定义了一个 doubleCountAddOne 的getter方法,在该方法中,我们通过this去调用了另一个getter方法 doubleCount,这里的this指向的是store实例。
// src/views/home.vue
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1
}),
getters: {
doubleCount: (state) => state.count * 2,
doubleCountAddOne() {
return this.doubleCount + 1
}
}
})
export default useCounter
- getters也支持返回一个函数,这样就可以接收参数。
什么是Pinia?
- Pinia本质上依然是一个
状态管理的库
,它允许你跨组件/页面
进行状态共享。
Pinia和Vuex的区别?
- 与Vuex相比,Pinia提供了一个
更简单的API
,具有更少的规范
,提供了Composition-API风格的API
。 - 最重要的是,在与
TypeScript
一起使用时具有可靠的类型推断支持
。
和Vuex相比,Pinia有什么优势?
- mutations不再存在,只有state,gettes,actions。
- 更友好的TypeScript支持。
- 不再有modules的嵌套结构,每个store都是独立的,互不影响。
- 没有命名空间模块。
- 无需动态添加 Store,默认情况下它们都是动态的。
- 不再需要注入、导入函数、调用函数。
- 支持插件来扩展自身功能。
- 支持服务端渲染(SSR)。
上面列出的只是Pinia的一些主要的优点,还有其它的优点,需要大家在使用的时候慢慢去体会,下面一起来看看Pinia是如何使用的。
如何使用Pinia?
安装Pinia
yarn add pinia
或者
npm install pinia
创建Pinia
// src/stores/index.js
import { createPinia } from "pinia"
const pinia = createPinia ()
export default pinia
挂载Pinia
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './stores'
const app = createApp(App)
app.use(pinia)
app.mount('#app')
定义一个Store
- 我们需要知道 Store 是使用
defineStore()
定义的。 - 并且它需要一个
唯一名称
,作为第一个参数传递。 - 这个name,也称为id,是必需的,Pinia使用它来将store连接到devtools。
- 返回的函数统一使用
useXXX
作为命名方案,这是约定的一种规范。
// src/stores/counter.js
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1
})
})
export default useCounter
使用Store
- store在它被使用之前是不会被创建的,我们可以通过use函数来使用store。
// src/views/Home.vue
<script setup>
import useCounter from '@/stores/counter'
const counterStore = useCounter()
</script>
<template>
<div class="home">
<h2>count: {{ counterStore.count }}</h2>
</div>
</template>
解构Store
注意:
- 使用传统的方式将属性从store中解构出来,虽然可以获取到值,但是不具有响应式。
// src/views/Home.vue
<script setup>
import useCounter from '@/stores/counter'
const counterStore = useCounter()
// 将counter从store中解构出来,此时我们再去修改count,count的值可以修改,但是页面的值不会发生变化,不具有响应式
const { count } = counterStore
cosnt incrementCount = () => {
counterStore.count++
}
</script>
<template>
<div class="home">
<!-- count值不会随着counter++响应式更新 -->
<h2>count: {{ counterStore.count }}</h2>
<button @click="incrementCount">count++</button>
</div>
</template>
- 为了从store中解构属性的时候保持其响应式,我们可以使用pinia提供的
storeToRefs
函数。
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
// 使用storeToRefs解构
const { count } = storeToRefs(counterStore)
cosnt incrementCount = () => {
counterStore.count++
}
</script>
<template>
<div class="home">
<!-- 使用storeToRefs解构 -->
<h2>count: {{ count }}</h2>
<button @click="incrementCount">count++</button>
</div>
</template>
- 或者使用vue提供的
toRefs
函数。
// src/views/Home.vue
<script setup>
import { toRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
// 使用toRefs 解构
const { count } = toRefs(counterStore)
cosnt incrementCount = () => {
counterStore.count++
}
</script>
<template>
<div class="home">
<!-- 使用toRefs 解构 -->
<h2>count: {{ count }}</h2>
<button @click="incrementCount">count++</button>
</div>
</template>
Pinia中操作state
- 默认情况下,可以通过
store.属性名
来修改state。 - 可以通过调用store上的
$reset()
方法将state重置到其初始值。 - 如果想要一次修改多个state,可以调用store上的
$patch
方法。
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useUser from '@/stores/user'
const userStore = useUser()
const { name, age, height } = storeToRefs(userStore)
// 修改state,一个个修改
cosnt changeState = () => {
userStore.name = "jackson"
userStore.age = 25
userStore.height = 1.8
}
// 重置state
const resetState = () => {
userStore.$reset()
}
// 修改state,一次修改多个
const changeMoreState = () => {
userStore.$patch({
name: "jackson",
age: 25,
height: 1.8
})
}
</script>
<template>
<div class="home">
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
<h2>height: {{ height }}</h2>
<button @click="changeState">修改state</button>
<button @click="resetState">重置state</button>
<button @click="changeMoreState">修改多个state</button>
</div>
</template>
// sre/stores/user.js
import { defineStore } from 'pinia'
const useUser = defineStore("user", {
state: () => ({
name: "james",
age: 18,
height: 1.7
})
})
export default useUser
Pinia中的getters
定义和访问getter
- getters相当于store的计算属性。
- getters中可以接收一个state作为参数的函数。
// sre/stores/counter.js
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1,
firstName: "Jackson",
lastName: "vision",
}),
getters: {
doubleCount: (state) => state.count * 2,
fullName: (state) => `${state.firstName} ${state.lastName}`,
}
})
export default useCounter
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
</script>
<template>
<div class="home">
<h2>doubleCount: {{ counterStore.doubleCount }}</h2>
<h2>fullName: {{ counterStore.fullName}}</h2>
</div>
</template>
- 在一个getters方法中也可以调用其它的getters方法。下图中,我们定义了一个 doubleCountAddOne 的getter方法,在该方法中,我们通过this去调用了另一个getter方法 doubleCount,这里的this指向的是store实例。
// src/views/home.vue
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1
}),
getters: {
doubleCount: (state) => state.count * 2,
doubleCountAddOne() {
return this.doubleCount + 1
}
}
})
export default useCounter
- getters也支持返回一个函数,这样就可以接收参数。
// src/stores/user.js
import { defineStore } from 'pinia'
const useUser = defineStore("user", {
state: () => ({
users: [
{ id: 1, name: "james", age: 38 },
{ id: 2, name: "jackson", age: 25 },
{ id: 3, name: "kurry", age: 34 },
]
}),
getters: {
getUserById: (state) => {
return userId => {
return state.users.find(item => item.id === userId)
}
}
}
})
export default useUser
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useUser from '@/stores/user'
const userStore = useUser()
</script>
<template>
<div class="home">
<h2>查找userId等于1的用户:{{ userStore.getUsersById(1) }}</h2>
</div>
</template>
- getters也可以访问其它store中的getters。
// sre/stores/counter.js
import { defineStore } from 'pinia'
import useUser from './user'
const useCounter = defineStore("counter", {
state: () => ({
count: 1
}),
getters: {
showMessage: (state) => {
const userStore = useUser()
return `name: ${ userStore.name } - count: ${ state.count }`
}
}
})
export default useCounter
// sre/stores/user.js
import { defineStore } from 'pinia'
const useUser = defineStore("user", {
state: () => ({
name: "james",
})
})
export default useUser
// src/views/Home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
</script>
<template>
<div class="home">
<h2>showMessage:{{ counterStore.showMessage }}</h2>
</div>
</template>
Pinia中的actions
- actions相当于组件中的methods。
- 和getters一样,在action中可以通过this访问整个store实例的所有操作。
- 并且actions中是支持异步操作的,我们可以在actions中编写异步函数,在函数中使用await。
// src/stores/counter.js
import { defineStore } from 'pinia'
const useCounter = defineStore("counter", {
state: () => ({
count: 1
}),
actions: {
increment() {
this.count++
}
}
})
export default useCounter
// src/views/home.vue
<script setup>
import { storeToRefs } from 'pinia'
import useCounter from '@/stores/counter'
const counterStore = useCounter()
const changeCount = () => {
counterStore.increment()
}
</script>
<template>
<div class="home">
<h2>count: {{ counterStore.count }}</h2>
<button @click="changeCount">修改count</button>
</div>
</template>
总结
总的来说,Pinia的语法更加的简洁,极易上手,学起来吧!