(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)
目录
Vue3.3新特性
defineOptions
defineModel
pinia
介绍
与 Vuex 3.x/4.x 的比较
安装
核心概念
定义 Store
Option Store
Setup Store
storeToRefs
State
Getter
Action
pinia-plugin-persistedstate
概述
安装
用法
Vue3.3新特性
defineOptions
背景说明:有 <script setup> 之前,如果要定义 props, emits 可以轻而易举地添加一个与 setup 平级的属性。但是用了 <script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性。为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。但这只解决了 props 与 emits 这两个属性。如果我们要定义组件的 name 或其他自定义的属性,还是得回到最原始的用法——再添加一个普通的 <script> 标签。这样就会存在两个 <script> 标签。让人无法接受
所以在 Vue 3.3 中新引入了 defineOptions 宏。顾名思义,主要是用来定义 Options API 的选项。可以用 defineOptions 定义任意的选项, props, emits, expose, slots 除外(因为这些可以使用 defineXXX 来做到)
defineModel
在 Vue3 中,自定义组件上使用 v-model, 相当于传递一个 modelValue 属性,同时触发 update:modelValue 事件我们需要先定义 props,再定义 emits 。其中有许多重复的代码。如果需要修改此值,还需要手动调用 emit 函数。
新的defineModel宏的使用。宏会自动注册一个Props,并返回一个可以直接突变的引用:
注意:直接使用 defineModel 宏会报错,需要开启。
pinia
介绍
Pinia 是 Vue 的最新状态管理工具,是vuex的替代品
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
与 Vuex 3.x/4.x 的比较
Pinia API 与 Vuex ≤4 有很大不同,即:
- mutations 不再存在。他们经常被认为是 非常 冗长。他们最初带来了 devtools 集成,但这不再是问题。
- 无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
- 不再需要注入、导入函数、调用函数、享受自动完成功能!
- 无需动态添加 Store,默认情况下它们都是动态的,您甚至都不会注意到。请注意,您仍然可以随时手动使用 Store 进行注册,但因为它是自动的,您无需担心。
- 不再有 modules 的嵌套结构。您仍然可以通过在另一个 Store 中导入和 使用 来隐式嵌套 Store,但 Pinia 通过设计提供平面结构,同时仍然支持 Store 之间的交叉组合方式。 您甚至可以拥有 Store 的循环依赖关系。
- 没有 命名空间模块。鉴于 Store 的扁平架构,“命名空间” Store 是其定义方式所固有的,您可以说所有 Store 都是命名空间的。
安装
1.使用vite创建一个空的vue3项目
npm create vue@latest
2.用你喜欢的包管理器安装pinia:
yarn add pinia
# 或者使用 npm
npm install pinia
3.创建一个 pinia 实例 (根 store) 并将其传递给应用:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'const pinia = createPinia()
const app = createApp(App)app.use(pinia)
app.mount('#app')
核心概念
定义 Store
在深入研究核心概念之前,我们得知道 Store 是用
defineStore()
定义的,它的第一个参数要求是一个独一无二的名字:
import { defineStore } from 'pinia'// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {// 其他配置...
})
这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use... 是一个符合组合式函数风格的约定。
defineStore()
的第二个参数可接受两类值:Setup 函数或 Option 对象。
Option Store
与 Vue 的选项式 API 类似,我们也可以传入一个带有
state
、actions
与getters
属性的 Option 对象
export const useCounterStore = defineStore('counter', {state: () => ({ count: 0 }),getters: {double: (state) => state.count * 2,},actions: {increment() {this.count++},},
})
你可以认为
state
是 store 的数据 (data
),getters
是 store 的计算属性 (computed
),而actions
则是方法 (methods
)。
Setup Store
与 Vue 组合式 API 的 setup 函数相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
export const useCounterStore = defineStore('counter', () => {const count = ref(0)function increment() {count.value++}return { count, increment }
})
在 Setup Store 中:
ref()
就是state
属性computed()
就是getters
function()
就是actions
storeToRefs
请注意,
store
是一个用reactive
包装的对象,这意味着不需要在 getters 后面写.value
,就像setup
中的props
一样,如果你写了,我们也不能解构它:
<script setup>
const store = useCounterStore()
// ❌ 这将不起作用,因为它破坏了响应性
// 这就和直接解构 `props` 一样
const { name, doubleCount } = store
name // 将始终是 "Eduardo"
doubleCount // 将始终是 0
setTimeout(() => {store.increment()
}, 1000)
// ✅ 这样写是响应式的
// 💡 当然你也可以直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
</script>
为了从 store 中提取属性时保持其响应性,你需要使用
storeToRefs()
。它将为每一个响应式属性创建引用。当你只使用 store 的状态而不调用任何 action 时,它会非常有用。请注意,你可以直接从 store 中解构 action,因为它们也被绑定到 store 上:
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是响应式的 ref
// 同时通过插件添加的属性也会被提取为 ref
// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性
const { name, doubleCount } = storeToRefs(store)
// 作为 action 的 increment 可以直接解构
const { increment } = store
</script>
State
在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。
import { defineStore } from 'pinia'const useStore = defineStore('storeId', {// 为了完整类型推理,推荐使用箭头函数state: () => {return {// 所有这些属性都将自动推断出它们的类型count: 0,name: 'Eduardo',isAdmin: true,items: [],hasChanged: true,}},
})
默认情况下,你可以通过
store
实例访问 state,直接对其进行读写。
const store = useStore()store.count++
Getter
Getter 完全等同于 store 的 state 的计算值。可以通过
defineStore()
中的getters
属性来定义它们。推荐使用箭头函数,并且它将接收state
作为第一个参数:
export const useStore = defineStore('main', {state: () => ({count: 0,}),getters: {doubleCount: (state) => state.count * 2,},
})
然后你可以直接访问 store 实例上的 getter 了:
<script setup>
import { useStore } from './counterStore'
const store = useStore()
</script>
<template><p>Double count is {{ store.doubleCount }}</p>
</template>
Action
Action 相当于组件中的 method。它们可以通过
defineStore()
中的actions
属性来定义,并且它们也是定义业务逻辑的完美选择。
export const useCounterStore = defineStore('main', {state: () => ({count: 0,}),actions: {increment() {this.count++},randomizeCounter() {this.count = Math.round(100 * Math.random())},},
})
类似 getter,action 也可通过
this
访问整个 store 实例,并支持完整的类型标注(以及自动补全)。不同的是,action
可以是异步的,你可以在它们里面await
调用任何 API,以及其他 action!下面是一个使用 Mande 的例子。请注意,你使用什么库并不重要,只要你得到的是一个Promise
,你甚至可以 (在浏览器中) 使用原生fetch
函数:
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}},},
})
Action 可以像函数或者通常意义上的方法一样被调用:
<script setup>
const store = useCounterStore()
// 将 action 作为 store 的方法进行调用
store.randomizeCounter()
</script>
<template><!-- 即使在模板中也可以 --><button @click="store.randomizeCounter()">Randomize</button>
</template>
pinia-plugin-persistedstate
概述
本插件兼容
pinia^2.0.0
,在使用之前请确保你已经 安装 Pinia。pinia-plugin-persistedstate
丰富的功能可以使 Pinia Store 的持久化更易配置:
安装
1.用你喜欢的包管理器安装依赖
- pnpm:
pnpm i pinia-plugin-persistedstate
- npm:
npm i pinia-plugin-persistedstate
- yarn:
yarn add pinia-plugin-persistedstate
2.将插件添加到 pinia 实例上
import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia() pinia.use(piniaPluginPersistedstate)
用法
创建 Store 时,将
persist
选项设置为true
。
使用组合式 Store 语法:
import { defineStore } from 'pinia'export const useStore = defineStore('main',() => {const someState = ref('你好 pinia')return { someState }},{persist: true,}
)