1. Vue 组件通信:props 和 $emit 详解
props 和 $emit 是 Vue 组件间通信的核心机制,实现了父子组件间的数据双向流动。
props:父组件向子组件传值
props 是父组件向子组件传递数据的接口。
基本用法
html
xml
<!-- 父组件 Parent.vue -->
<template>
<ChildComponent :title="parentTitle" :count="num" />
</template>
<script>
export default {
data() {
return {
parentTitle: '来自父组件的标题',
num: 42
}
}
}
</script>
<!-- 子组件 ChildComponent.vue -->
<template>
<div>
<h2>{{ title }}</h2>
<p>计数: {{ count }}</p>
</div>
</template>
<script>
export default {
props: ['title', 'count'] // 简单声明
// 或者带类型检查
// props: {
// title: String,
// count: Number
// }
}
</script>
高级 props 配置
javascript
dart
props: {
// 基础类型检查
age: Number,
// 多个可能的类型
id: [String, Number],
// 必填项
username: {
type: String,
required: true
},
// 默认值
theme: {
type: String,
default: 'light'
},
// 对象/数组的默认值应当从工厂函数返回
config: {
type: Object,
default: () => ({ color: 'red' })
},
// 自定义验证函数
score: {
validator: value => {
return value >= 0 && value <= 100
}
}
}
$emit:子组件向父组件通信
$emit 允许子组件触发父组件中监听的事件。
基本用法
html
xml
<!-- 子组件 ChildComponent.vue -->
<template>
<button @click="notifyParent">通知父组件</button>
</template>
<script>
export default {
methods: {
notifyParent() {
this.$emit('child-event', '这是子组件的数据')
}
}
}
</script>
<!-- 父组件 Parent.vue -->
<template>
<ChildComponent @child-event="handleChildEvent" />
</template>
<script>
export default {
methods: {
handleChildEvent(dataFromChild) {
console.log('收到子组件数据:', dataFromChild)
}
}
}
</script>
带参数的 $emit
javascript
kotlin
// 子组件中触发
this.$emit('update', { key: 'value' }, 123)
// 父组件中接收
<ChildComponent @update="(payload, num) => handleUpdate(payload, num)" />
自定义事件验证 (Vue 3+)
javascript
javascript
emits: {
// 无验证
click: null,
// 带验证
submit: payload => {
if (payload.email && payload.password) {
return true
}
console.warn('无效的提交参数!')
return false
}
}
props 和 $emit 配合实现双向绑定
html
xml
<!-- 父组件 -->
<template>
<ChildComponent :value="parentValue" @input="parentValue = $event" />
<!-- 简写 -->
<ChildComponent v-model="parentValue" />
</template>
<!-- 子组件 -->
<template>
<input
:value="value"
@input="$emit('input', $event.target.value)"
/>
</template>
<script>
export default {
props: ['value']
}
</script>
Vue 3 中的变化
-
v-model 升级:
- 默认使用
modelValue
作为 prop - 使用
update:modelValue
作为事件 - 支持多个 v-model
- 默认使用
html
xml
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- 等价于 -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
-
emits 选项:
- 显式声明组件会触发的事件
- 更好的文档化和类型检查
最佳实践
- props 应该只用于向下传递数据
- 避免直接修改 props(使用 data 或 computed 包装)
- 事件命名使用 kebab-case (如
update-value
) - 复杂对象应该深拷贝后再修改
- 对于表单组件,实现 v-model 接口
与其它通信方式的对比
特性 | props/$emit | provide/inject | Event Bus | Vuex/Pinia |
---|---|---|---|---|
通信方向 | 父子组件双向 | 祖先→后代单向 | 任意组件 | 全局状态 |
适用层级 | 直接父子 | 跨多级 | 任意 | 全局 |
复杂度 | 低 | 中 | 中 | 高 |
类型支持 | 优秀 | 一般 | 差 | 优秀 |
适合场景 | 紧密耦合的父子组件 | 主题/配置等 | 简单应用 | 复杂应用 |
实际应用示例
自定义输入组件
html
xml
<!-- Parent.vue -->
<template>
<CustomInput v-model="username" />
</template>
<!-- CustomInput.vue -->
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
</template>
<script>
export default {
props: ['modelValue'],
emits: ['update:modelValue']
}
</script>
复杂数据传递
html
xml
<!-- Parent.vue -->
<template>
<UserForm
:initial-data="userData"
@submit="handleSubmit"
/>
</template>
<!-- UserForm.vue -->
<template>
<form @submit.prevent="submitForm">
<!-- 表单字段 -->
</form>
</template>
<script>
export default {
props: {
initialData: {
type: Object,
required: true
}
},
data() {
return {
formData: { ...this.initialData } // 创建副本
}
},
methods: {
submitForm() {
this.$emit('submit', this.formData)
}
}
}
</script>
props 和 $emit 构成了 Vue 组件通信的基础,理解它们的运作机制对于构建可维护的 Vue 应用至关重要。
2. Vue 中使用 ref
实现组件传值
ref
是 Vue 提供的一种特殊的 attribute,用于在父组件中直接访问子组件的实例或 DOM 元素。这种方式可以实现父组件向子组件传值,也可以实现子组件向父组件暴露方法或数据。
基本用法
1. 父组件访问子组件
html
xml
<!-- 父组件 Parent.vue -->
<template>
<ChildComponent ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: { ChildComponent },
methods: {
callChildMethod() {
// 通过ref访问子组件实例
this.$refs.childRef.childMethod()
// 访问子组件数据
console.log(this.$refs.childRef.childData)
}
}
}
</script>
2. 子组件暴露数据/方法
html
xml
<!-- 子组件 ChildComponent.vue -->
<template>
<div>{{ childData }}</div>
</template>
<script>
export default {
data() {
return {
childData: '子组件数据'
}
},
methods: {
childMethod() {
console.log('子组件方法被调用')
this.childData = '数据已更新'
}
}
}
</script>
主要应用场景
-
父组件调用子组件方法
javascript
kotlinthis.$refs.childRef.methodName()
-
访问子组件数据
javascript
kotlinthis.$refs.childRef.dataProperty
-
操作DOM元素
html
xml<input ref="inputRef" /> <script> this.$refs.inputRef.focus() </script>
-
表单验证 - 父组件触发子组件验证
javascript
kotlinthis.$refs.formRef.validate()
与 props/emit 的对比
特性 | ref 访问 | props/emit |
---|---|---|
通信方向 | 父→子(单向) | 双向(父↔子) |
数据类型 | 可以访问整个组件实例 | 只能传递简单数据 |
实时性 | 实时访问 | props需要显式更新 |
适用场景 | 需要直接操作子组件 | 标准的数据流通信 |
组件耦合度 | 较高(父组件需知道子组件结构) | 较低(通过接口通信) |
组合式API中的用法(Vue 3)
html
xml
<!-- 父组件 -->
<template>
<ChildComponent ref="childRef" />
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const childRef = ref(null)
// 访问子组件
const callChildMethod = () => {
childRef.value.childMethod()
}
</script>
最佳实践
-
适度使用:优先考虑 props/emit,只有必要时才用 ref
-
明确接口:子组件应明确哪些数据/方法可以被外部访问
-
避免滥用:不要过度依赖 ref 来修改子组件状态
-
类型安全(Vue 3 + TS):
typescript
typescript// 子组件 defineExpose({ childMethod: () => void, childData: string }) // 父组件 const childRef = ref<{ childMethod: () => void childData: string }>()
实际应用示例
表单提交案例
html
xml
<!-- 父组件 -->
<template>
<UserForm ref="userForm" />
<button @click="submit">提交表单</button>
</template>
<script>
export default {
methods: {
submit() {
if (this.$refs.userForm.validate()) {
const formData = this.$refs.userForm.getData()
// 提交逻辑...
}
}
}
}
</script>
<!-- 子组件 UserForm.vue -->
<script>
export default {
methods: {
validate() {
// 验证逻辑...
return isValid
},
getData() {
return { /* 表单数据 */ }
}
}
}
</script>
注意事项
- ref 不是响应式的 :不能在模板中直接绑定
$refs.xxx
- 渲染时机:ref 只在组件渲染完成后填充
- 数组情况 :当 ref 用在 v-for 中时,
$refs
会是一个数组 - 避免直接修改:直接修改子组件状态会使数据流难以追踪
总结
ref
组件传值提供了一种直接的组件通信方式,特别适合以下场景:
- 需要访问子组件DOM元素
- 需要调用子组件暴露的方法
- 需要获取子组件内部状态
- 实现表单验证等特定交互模式
但在大多数情况下,仍建议优先使用 props 和 emit 来维护清晰的数据流。
3. Event Bus(事件总线)详解
Event Bus 是 Vue 中实现组件间通信的一种模式,特别是在非父子组件 或兄弟组件之间传递数据时非常有用。
基本概念
Event Bus 是一个独立的 Vue 实例,作为中央事件处理器,允许组件:
- 发布(触发)事件
- 订阅(监听)事件
- 取消订阅(移除监听)
创建 Event Bus
Vue 2 创建方式
javascript
javascript
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
Vue 3 创建方式
由于 Vue 3 不再暴露 EventEmitter API,可以使用第三方库或自己实现:
javascript
javascript
// event-bus.js
import { emitter } from 'mitt' // 或者使用 tiny-emitter
export const EventBus = emitter()
核心 API
-
触发事件(发送消息)
javascript
bashEventBus.$emit('事件名称', 数据)
-
监听事件(接收消息)
javascript
javascriptEventBus.$on('事件名称', (数据) => { // 处理逻辑 })
-
一次性监听
javascript
bashEventBus.$once('事件名称', callback)
-
移除监听
javascript
bashEventBus.$off('事件名称', callback)
使用示例
组件A(发送事件)
javascript
javascript
import { EventBus } from './event-bus.js'
export default {
methods: {
sendMessage() {
EventBus.$emit('message-sent', {
text: 'Hello from Component A',
time: new Date()
})
}
}
}
组件B(接收事件)
javascript
javascript
import { EventBus } from './event-bus.js'
export default {
created() {
EventBus.$on('message-sent', this.handleMessage)
},
beforeDestroy() {
EventBus.$off('message-sent', this.handleMessage)
},
methods: {
handleMessage(payload) {
console.log('收到消息:', payload.text)
console.log('发送时间:', payload.time)
}
}
}
优缺点分析
优点
- 解耦组件:组件之间不需要直接引用
- 跨层级通信:任意组件间都可以通信
- 简单易用:适合小型应用或简单场景
缺点
- 难以追踪:事件流不如 props/emit 直观
- 内存泄漏风险:忘记移除监听会导致内存泄漏
- 不适合复杂场景:大型应用可能导致事件混乱
最佳实践
-
始终在组件销毁时移除监听
javascript
javascriptbeforeDestroy() { EventBus.$off('event-name', this.callback) }
-
使用命名规范
javascript
php// 好的命名 EventBus.$emit('user:logged-in', user) // 不好的命名 EventBus.$emit('update', data)
-
考虑使用 TypeScript 增强类型安全
typescript
go// 定义事件类型 type EventBusEvents = { 'user:logged-in': User 'cart:updated': CartItem[] } // 创建有类型的事件总线 const EventBus: Emitter<EventBusEvents> = mitt()
替代方案比较
方案 | 适用场景 | 复杂度 | 可维护性 |
---|---|---|---|
Event Bus | 简单应用/少量组件通信 | 低 | 中 |
Vuex/Pinia | 中大型应用/状态管理 | 中高 | 高 |
provide/inject | 祖先-后代组件通信 | 中 | 中高 |
parent/root | 紧密耦合的组件 | 低 | 低 |
实际应用场景
-
用户登录状态通知
javascript
csharp// 登录成功后 EventBus.$emit('auth:logged-in', user) // 导航栏组件更新 EventBus.$on('auth:logged-in', this.updateUserProfile)
-
全局通知/提示
javascript
csharp// 触发错误提示 EventBus.$emit('notification:error', '操作失败') // 在根组件监听并显示提示 EventBus.$on('notification:error', this.showError)
-
表单跨组件验证
javascript
csharp// 提交时验证所有字段 EventBus.$emit('form:validate') // 各个表单字段组件监听验证事件 EventBus.$on('form:validate', this.validateField)
总结
Event Bus 是 Vue 组件通信的有力工具,特别适合:
- 小型应用
- 非父子组件通信
- 需要快速实现通信的场景
但在大型应用中,建议使用 Vuex/Pinia 等状态管理方案,以保持更好的可维护性和可追踪性。
4. 解释 $parent
和 $root
在 Vue.js 中,$parent
和 $root
是组件实例的两个属性,用于访问组件树中的父级和根实例。
$parent
$parent
属性可以访问当前组件的直接父组件实例。
特点:
- 指向当前组件的直接父组件
- 如果当前组件没有父组件(如根组件),则
$parent
为undefined
- 可以通过它访问父组件的 data、methods 等
使用示例:
javascript
kotlin
// 子组件中
this.$parent.parentMethod() // 调用父组件的方法
this.$parent.parentData // 访问父组件的数据
注意事项:
- 紧耦合:会使子组件与特定父组件紧密耦合,降低复用性
- 不推荐:Vue 官方建议优先使用 props/events 进行父子通信
- 适用场景:开发可复用组件库时可能有用,但普通应用开发中应谨慎使用
$root
$root
属性可以访问当前组件树的根 Vue 实例。
特点:
- 指向当前组件树的根实例(通常是
new Vue()
创建的实例) - 在单组件应用中,所有组件的
$root
都指向同一个根实例 - 可以通过它访问根实例的 data、methods 等
使用示例:
javascript
kotlin
// 任何子组件中
this.$root.rootMethod() // 调用根实例的方法
this.$root.globalData // 访问根实例的数据
注意事项:
- 全局状态:虽然可以用于共享全局状态,但 Vuex/Pinia 是更好的选择
- 命名冲突:大量使用可能导致命名冲突
- 测试困难:会使组件更难独立测试
对比表格
特性 | $parent |
$root |
---|---|---|
指向对象 | 直接父组件 | 根 Vue 实例 |
层级关系 | 只上一级 | 直接到顶级 |
使用频率 | 较少 | 较少 |
推荐替代方案 | props/events | Vuex/Pinia/Provide-Inject |
适用场景 | 特定父子组件紧密交互 | 需要访问最顶层全局设置/状态 |
替代方案建议
-
父子通信:
- 向下传递:使用
props
- 向上传递:使用
$emit
事件
- 向下传递:使用
-
跨层级/全局状态:
- 使用
provide/inject
- 使用状态管理库(Vuex/Pinia)
- 使用
-
兄弟组件通信:
- 通过共同的父组件中转
- 使用事件总线(Event Bus)
- 使用状态管理库
实际应用场景
$parent
可能的使用场景:
- 表单组件中,子表单控件需要访问父表单的方法
- 自定义组件库中,特定结构的组件需要相互访问
$root
可能的使用场景:
- 访问全局配置(如 API 基础URL)
- 在小型应用中替代状态管理(不推荐大型项目)
代码示例
javascript
javascript
// 根实例 (main.js)
new Vue({
data: { appName: 'My App' },
methods: { globalAlert() { alert('Global') } }
})
// 父组件
export default {
data() { return { parentMsg: 'Hello' } },
methods: { parentMethod() { console.log('Parent method') } }
}
// 子组件
export default {
mounted() {
console.log(this.$parent.parentMsg) // 访问父组件数据
this.$parent.parentMethod() // 调用父组件方法
console.log(this.$root.appName) // 访问根实例数据
this.$root.globalAlert() // 调用根实例方法
}
}
虽然 $parent
和 $root
提供了方便的访问方式,但在大多数情况下,更推荐使用显式的 props/events 或状态管理来实现组件通信,这样代码更清晰、更易于维护。
5. 解释 attrs
和 listeners
在 Vue.js 中,$attrs
和 $listeners
是组件实例的两个特殊属性,用于处理属性和事件的传递。
$attrs
$attrs
包含了父组件传递给子组件的所有 attribute(属性),但以下情况除外:
- 已经在子组件的
props
中声明的属性 - 被
class
和style
属性(这两个属性有特殊处理)
使用场景
- 当你需要将父组件传递的所有属性(除了已声明的 props)传递给更深层次的子组件时
- 创建高阶组件或包装组件时
示例
html
xml
<!-- 父组件 -->
<child-component id="my-id" data-test="123" title="示例"></child-component>
<!-- 子组件 -->
<template>
<div v-bind="$attrs"> <!-- 这里会接收所有未声明的属性 -->
内容
</div>
</template>
$listeners
$listeners
包含了父组件传递给子组件的所有事件监听器(不包括带有 .native
修饰符的原生事件)。
使用场景
- 当你需要将父组件传递的所有事件监听器传递给更深层次的子组件时
- 创建透明包装组件时
示例
html
xml
<!-- 父组件 -->
<child-component @click="handleClick" @custom="handleCustom"></child-component>
<!-- 子组件 -->
<template>
<button v-on="$listeners"> <!-- 这里会接收所有事件监听器 -->
点击我
</button>
</template>
组合使用
通常在创建高阶组件时,会同时使用 $attrs
和 $listeners
:
html
bash
<template>
<base-component v-bind="$attrs" v-on="$listeners"></base-component>
</template>
Vue 2 和 Vue 3 的区别
在 Vue 3 中:
$listeners
被移除,事件监听器现在是$attrs
的一部分$attrs
现在包含所有传递的属性和事件监听器(包括class
和style
)
这些特性在组件间传递属性和事件时非常有用,特别是在创建可复用组件或高阶组件时。
6. 解释 provide
和 inject
provide
和 inject
是 Vue.js 提供的一对 API,用于实现跨层级组件通信(主要是祖先组件向后代组件传递数据),而不需要通过 props 逐层传递。
provide
(提供)
provide
选项/方法允许祖先组件向其所有子孙后代组件提供数据或方法,无论组件层次有多深。
使用方式
选项式 API:
javascript
javascript
// 祖先组件
export default {
provide: {
theme: 'dark', // 静态值
user: this.userData // 如果使用 this,provide 必须是一个函数
},
// 或者使用函数形式
provide() {
return {
theme: 'dark',
user: this.userData // 可以访问组件实例
}
}
}
组合式 API:
javascript
javascript
import { provide } from 'vue'
setup() {
const theme = ref('dark')
provide('theme', theme) // 提供响应式数据
}
inject
(注入)
inject
选项/方法允许后代组件接收祖先组件通过 provide
提供的数据。
使用方式
选项式 API:
javascript
arduino
// 后代组件
export default {
inject: ['theme'], // 简单注入
// 或者带默认值和重命名
inject: {
themeColor: {
from: 'theme', // 从哪个 provide 属性获取
default: 'light' // 默认值
}
}
}
组合式 API:
javascript
javascript
import { inject } from 'vue'
setup() {
const theme = inject('theme', 'light') // 第二个参数是默认值
}
特点
-
跨层级通信:可以跨越任意层级的组件传递数据
-
响应式:如果提供的是响应式数据(如 ref/reactive),注入的组件也会响应变化
-
非响应式:普通值不是响应式的,除非使用响应式 API 包装
-
主要应用场景:
- 主题/样式配置
- 国际化
- 全局状态(简单场景)
- 表单组件嵌套
示例
javascript
javascript
// 祖先组件
export default {
provide() {
return {
theme: {
primaryColor: 'blue',
textColor: 'black'
}
}
}
}
// 后代组件
export default {
inject: ['theme'],
created() {
console.log(this.theme.primaryColor) // 'blue'
}
}
与 props 的区别
- 方向性:props 是父向子,provide/inject 是祖先向后代
- 层级:props 需要逐层传递,provide/inject 可以跨层级
- 显隐性:props 是显式声明,provide/inject 是隐式依赖
注意事项
- 过度使用会使组件关系变得不透明,难以追踪数据来源
- 不是响应式的(除非你提供的是响应式对象)
- 主要用于开发高阶组件/插件,普通组件通信优先考虑 props/emit 或 Vuex/Pinia
在 Vue 3 中,provide
和 inject
更加强大,可以配合 Composition API 提供响应式数据。
7. Vuex 和 Pinia 详解:Vue 状态管理方案
Vuex:传统的 Vue 状态管理
核心概念
Vuex 是 Vue 的官方状态管理库,采用集中式存储管理应用的所有组件的状态。
1. State - 数据源
javascript
yaml
state: {
count: 0,
user: null
}
2. Getters - 计算属性
javascript
css
getters: {
doubleCount: state => state.count * 2
}
3. Mutations - 同步修改状态
javascript
javascript
mutations: {
increment(state, payload) {
state.count += payload.amount
}
}
4. Actions - 异步操作
javascript
javascript
actions: {
async fetchUser({ commit }) {
const user = await api.getUser()
commit('SET_USER', user)
}
}
5. Modules - 模块化
javascript
css
const moduleA = {
state: () => ({ ... }),
mutations: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA
}
})
典型使用示例
javascript
javascript
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
cartItems: []
},
mutations: {
ADD_TO_CART(state, product) {
state.cartItems.push(product)
}
},
actions: {
addProductToCart({ commit }, product) {
commit('ADD_TO_CART', product)
}
},
getters: {
cartTotal: state => {
return state.cartItems.reduce((total, item) => total + item.price, 0)
}
}
})
Pinia:新一代 Vue 状态管理
Pinia 是 Vue 官方推荐的状态管理库,比 Vuex 更简单直观。
核心特点
- 更简单的 API - 没有 mutations,只有 state/getters/actions
- TypeScript 友好 - 完整的类型推断支持
- 模块化设计 - 每个 store 都是自动模块化的
- Composition API 风格 - 更好的组合式 API 支持
基本结构
javascript
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
async fetchData() {
const data = await api.getData()
this.updateData(data)
}
}
})
在组件中使用
javascript
javascript
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const counter = useCounterStore()
return {
// 访问 state
count: counter.count,
// 访问 getter
doubleCount: counter.doubleCount,
// 调用 action
increment: counter.increment
}
}
}
Vuex 和 Pinia 的对比
特性 | Vuex | Pinia |
---|---|---|
Vue 版本支持 | Vue 2/3 | Vue 3 (也支持 Vue 2.7+) |
API 复杂度 | 较高 (mutations/actions) | 较低 (只有 actions) |
TypeScript 支持 | 需要额外配置 | 原生支持 |
模块化 | 需要显式声明 modules | 自动模块化 |
Composition API | 兼容但不够自然 | 原生设计支持 |
DevTools 支持 | 完善 | 完善 |
包大小 | 较大 | 更轻量 |
学习曲线 | 较陡峭 | 平缓 |
迁移建议
- 新项目:优先选择 Pinia
- 现有 Vue 3 项目:逐步迁移到 Pinia
- Vue 2 项目:继续使用 Vuex 或升级到 Vue 2.7+ 使用 Pinia
Pinia 高级用法
1. 状态持久化
javascript
javascript
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
2. 组合式 Store
javascript
javascript
export const useUserStore = defineStore('user', () => {
// 相当于 state
const name = ref('')
const age = ref(0)
// 相当于 getters
const isAdult = computed(() => age.value >= 18)
// 相当于 actions
function updateUser(newName, newAge) {
name.value = newName
age.value = newAge
}
return { name, age, isAdult, updateUser }
})
3. Store 间交互
javascript
scss
const userStore = useUserStore()
const cartStore = useCartStore()
// 在一个 action 中调用另一个 store
function checkout() {
if (!userStore.isLoggedIn) {
userStore.login()
}
cartStore.clearCart()
}
最佳实践
- 合理划分 Store - 按功能模块而非数据类别划分
- 避免过度使用 - 只将真正需要共享的状态放入 Store
- 保持单向数据流 - 组件不应直接修改 store 状态
- 命名规范 - 使用
useXxxStore
命名约定 - 类型安全 - 充分利用 TypeScript 类型推断
总结
Vuex 和 Pinia 都解决了 Vue 应用中的状态管理问题,但 Pinia 提供了更现代、更简洁的 API 设计。对于新项目,Pinia 是更好的选择,它减少了样板代码,提高了开发效率,同时保持了与 Vue DevTools 的良好集成。