💕Pinia 注册
✔ vue3 与 Pinia 注册
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp()
app.use(createPinia())
app.mount('#app')
✔ vue2 与 Pinia 注册
import Vue from 'vue'
import App from './App.vue'
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
new Vue({
render: (h) => h(App),
createPinia()
}).$mount('#app');
💕定义与使用 Store
✔ 定义 Store
import { defineStore } from 'pinia'
// 第一个参数是应用程序中 store 的唯一 id
export const useUser = defineStore('user', {
state: () => {
return { name: 'Jack', age: 18 }
},
getters: {},
actions: {}
})
✔ 在 vue3 <script setup>
中使用
<script setup>
import { useUser } from '@/stores/User'
const user = useUser()
</script>
<template>
<div>{{ user.name }} - {{ user.age }}</div>
</template>
✔ 在 vue3 setup()
中使用
<script>
import { useUser } from '@/stores/User'
export default {
setup() {
const user = useUser()
return {
user
}
}
}
</script>
<template>
<div>{{ user.name }} - {{ user.age }}</div>
</template>
✔ 在 vue2
中使用
- 导入可读属性,无法修改
<script>
import { useUser } from '@/stores/User'
import { mapState } from 'pinia'
// 导入的是只读属性,无法进行修改
export default {
computed: {
...mapState(useUser, {
user: (store) => store,
// 自定义属性
ownName: 'age'
double: (store) => store.age * 2,
// 通过 this 读取自定义属性值
magicValue: (store) => {
return this.double + store.age + this.ownName
}
})
// 也可以采用这种方式
// ...mapState(useUser, ['age', 'name'])
// <div>{{ name }} - {{ age }}</div>
}
}
</script>
<template>
<button @click="age = 12">尝试修改 age</button>
<div>{{ user.name }} - {{ user.age }}</div>
</template>
- 导入可读可修改属性
<script>
import { useUser } from '@/stores/User'
import { mapWritableState } from 'pinia'
// 导入的是可修改属性
export default {
computed: {
...mapWritableState(useUser, {
user: (store) => store
})
}
}
</script>
<template>
<div>{{ user.name }} - {{ user.age }}</div>
</template>
✔ 使用效果
💕修改 Store
✔ 直接修改 store
<script setup>
import { useUser } from '@/stores/user'
const user = useUser() // user 是 reactive 对象
// 不能使用 { age, name } = user 进行解绑,具体看注意事项
const handle = () => {
// 直接修改
user.age++
}
</script>
<template>
<div>{{ user.age }} - {{ user.name }}</div>
</template>
✔ 通过 $patch 修改 store
- 接收变量的方式
<script setup>
import { useUser } from '@/stores/user'
const user = useUser()
const handle = () => {
user.$patch({
name: 'John',
age: 81
})
}
</script>
<template>
<button @click="handle">修改 user 信息</button>
<div>{{ user.age }} - {{ user.name }}</div>
</template>
- 接收函数的方式
<script setup>
import { useUser } from '@/stores/user'
const user = useUser()
const handle = () => {
user.$patch((state) => {
// 直接对 state 进行修改
state.age = 81
state.name = 'John'
})
}
</script>
<template>
<button @click="handle">修改 user 信息</button>
<div>{{ user.age }} - {{ user.name }}</div>
</template>
✔ 重置 state
import { useUser } from '@/stores/user'
const user = useUser()
// 重置 (初始状态)
user.$reset()
✔ 替换 state
import { useUser } from '@/stores/user'
const user = useUser()
store.$state = { name: 'Paimon', age: 12 }
💕 订阅 Store
可以通过 $subscribe
对 store
进行订阅。只会在 patches 之后触发一次
import { useUser } from '@/stores/user'
const user = useUser()
user.$subscribe((mutation, state) => {
console.log(mutation)
console.log(state)
})
✔ mutation.type
为 patch object
user.$patch({ name: 'John', age: '81' })
✔ mutation.type
为 patch function
user.$patch((state) => {
state.name = 'John'
state.age = '81'
})
✔ mutation.type
为 direct
user.name = 'John'
user.age = '81'
💕 定义与使用 Getters
✔ 定义 Getters
import { defineStore } from 'pinia'
import { useOtherStore } from '@/store/otherStore'
// 第一个参数是应用程序中 store 的唯一 id
export const useUser = defineStore('user', {
state: () => {
return { name: 'Jack', age: 18 }
},
getters: {
// 访问 state 的值
doubuleAge: (state) => state.age * 2,
// 访问 getters 的值(通过 this 进行访问)
doubleAgePlusOne: (state) => this.doubleAge + 1
// 访问其他 store 的 getters
otherstore: () => {
const otherStore = useOtherStore()
return state.other
}
},
actions: {}
})
✔ 访问 Getters
<script setup>
import { useUser } from '@/stores/User'
const user = useUser()
</script>
<template>
<div>{{ user.name }} - {{ user.age }} - {{ user.doubuleAge }} - {{ user.doubuleAgePlusOne }}</div>
</template>
💕 定义与使用 Actions
✔ 定义 Actions
非常适合定义业务逻辑
import { defineStore } from 'pinia'
import { useOtherStore } from '@/store/otherStore'
// 第一个参数是应用程序中 store 的唯一 id
export const useUser = defineStore('user', {
state: () => {
return { name: 'Jack', age: 18 }
},
getters: {},
actions: {
// 封装业务逻辑
async registerUser({ name, password }) {
const data = await api.post({ login, password })
this.state.name = data.name
this.state.age = data.age
}
// 访问其他的 store
async fetchUserPreferences() {
const other = useOtherStore()
// 获取到 other 的数据
}
}
})
actions
可以是异步的,您可以在其中await
任何 API 调用甚至其他操作!
✔ 访问 Actions
import { useUser } from '@/store/user'
const user = useUSer()
// vue3 的方法方式,vue2 的访问方式见注意事项
user.registerUser({
name: 'john'
password: '123'
})
💕 定阅 Actions
可以使用 store.$onAction()
订阅 actions 及其结果
const userStore = useUSer()
const unsubscribe = userStore.$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()
💕 Pinia 持久化配置
需要借助 pinia-plugin-persistedstate
import App from './App.vue'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const app = createApp(App)
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
// 组合式写法 具体看 注意事项
import { defineStore } from 'pinia'
const persist = {
key: 'storeKey',
storage: window.sessionStorage,
// 部分持久化状态的点符号路径数组,[]意味着没有状态被持久化(默认为undefined,持久化整个状态)
// paths: ['age']
}
export const useUser = defineStore('user', () => {
const name = ref('Jack')
const age = ref(18)
const session = JSON.parse(window.sessionStorage.getItem('storeKey'))
if (session) { // 判断 session 是否已经有值存在,实现持久化
name.value = session.name
age.value = session.age
}
return {
name, age
}
}, { persist })
💕 注意事项
✔ 选项式与组合式写法
// 选项式
import { defineStore } from 'pinia'
export const useUser = defineStore('user', {
state: () => {
return {
name: 'Jack',
age: 18
}
},
getters: {
doubuleAge: (state) => state.age * 2,
doubleAgePlusOne: (state) => this.doubleAge + 1
},
actions: {
async registerUser() {/*.......*/}
}
})
等价于:
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUser = defineStore('user', () => {
// state
const name = ref('Jack')
const age = ref(18)
// getters
const doubuleAge = ref()
doubuleAge.value = age.value * 2
const doubleAgePlusOne = ref()
doubleAgePlusOne.value = doubuleAge.value + 1
// actions
const registerUser = () => { /*.......*/ }
return {
name,
age,
doubuleAge,
doubleAgePlusOne,
registerUser
}
})
✔ 解构失去响应式
<script setup>
import { useUser } from '@/stores/user'
const user = useUser()
// user 是 reactive 对象
let { age, name } = user // 解构失去了响应式,不解构是不会失去响应式的
const handle = () => {
age++ // 修改值
console.log(age) // 修改后
}
</script>
<template>
<button @click="handle">尝试修改 age</button>
<div>{{ name }} - {{ age }}</div>
</template>
解决办法:使用 storeToRefs
,它将为任何响应式属性创建 refs
import { storeToRefs } from 'pinia'
import { useUser } from '@/stores/user'
const user = useUser()
let { age, name } = storeToRefs(user) // 有了响应式
const handle = () => {
age.value = age.value + 1 // 注意
}
✔ 取消订阅 Store
默认情况下,state subscriptions 绑定到添加它们的组件。当组件被卸载时,它们将被自动删除。如果要在卸载组件后保留它们,可以进行以下操作:
<script setup>
import { useUser } from '@/stores/user'
const user = useUser()
// 此订阅将在组件卸载后保留
user.$subscribe((mutation, state) => {
console.log(mutation)
console.log(state)
}, { detached: true })
</script>
✔ 取消订阅 Actions
默认情况下,action subscriptions 绑定到添加它们的组件。当组件被卸载时,它们将被自动删除。如果要在卸载组件后保留它们,可以进行以下操作:
<script setup>
import { useUser } from '@/stores/user'
const user = useUser()
// 此订阅将在组件卸载后保留
user.$onAction(() => {/* ..... */}, true)
</script>
✔ vue2 将 Actions 印射到组件
import { mapActions } from 'pinia'
import { useUser } from '@/stores/user'
export default {
methods: {
...mapActions(useUser, ['registerUser'])
...mapActions(useUser, { myOwnName: 'registerUser' }),
},
}
✔ 如何监听整个 Pinia 状态
import { createApp, watch } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp()
watch(pinia.state, (state) => {
// 每当它发生变化时,将整个状态持久化到本地存储
localStorage.setItem('piniaState', JSON.stringify(state))
},{ deep: true })
app.use(pinia)
app.mount('#app')