Vue3 选项式 API vs 组合式 API 选择指南
Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API。
两种风格的核心区别
选项式 API (Options API) ------ 类似面向对象封装
vue
<script>
export default {
// 选项分离
data() {
return {
count: 0,
message: 'Hello'
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
},
watch: {
count(newVal) {
console.log('count changed:', newVal)
}
},
mounted() {
console.log('组件已挂载')
}
}
</script>
组合式 API (Composition API) ------ 类似函数式编程
vue
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
// 逻辑聚合
const count = ref(0)
const message = ref('Hello')
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
watch(count, (newVal) => {
console.log('count changed:', newVal)
})
onMounted(() => {
console.log('组件已挂载')
})
</script>
选择决策树
markdown
选择哪种 API?
│
├── 你是 Vue 初学者?且习惯面向对象编程
│ ├── 是 → 选项式 API (更容易理解)
│ └── 否 → 继续下面的选择
│
├── 项目复杂度如何?
│ ├── 简单组件 → 任选其一
│ ├── 复杂组件 → 组合式 API
│ └── 大型项目 → 组合式 API
│
├── 团队经验如何?
│ ├── 都熟悉 Composition API → 组合式 API
│ ├── 有 React 经验 → 组合式 API
│ └── 传统前端背景 → 选项式 API
│
└── 需要逻辑复用?
├── 是 → 组合式 API
└── 否 → 任选其一
详细对比分析
1. 学习曲线对比
初学者友好度
javascript
// 选项式 API - 更符合传统面向对象思维
export default {
data() {
return {
// 数据都在这里,一目了然
users: [],
loading: false,
error: null
}
},
methods: {
// 方法都在这里,逻辑清晰
async fetchUsers() {
this.loading = true
try {
this.users = await api.getUsers()
} catch (err) {
this.error = err.message
} finally {
this.loading = false
}
}
}
}
// 组合式 API - 需要理解响应式系统
import { ref } from 'vue'
export default {
setup() {
// 需要理解 ref、reactive 等概念
const users = ref([])
const loading = ref(false)
const error = ref(null)
const fetchUsers = async () => {
loading.value = true // 注意要 .value
try {
users.value = await api.getUsers() // 注意要 .value
} catch (err) {
error.value = err.message // 注意要 .value
} finally {
loading.value = false // 注意要 .value
}
}
return {
users,
loading,
error,
fetchUsers
}
}
}
2. 逻辑组织对比
复杂组件中的逻辑组织
vue
<!-- 选项式 API - 相关逻辑被分散 -->
<script>
export default {
data() {
return {
// 用户相关数据
userName: '',
userEmail: '',
userAge: 0,
// 产品相关数据
productName: '',
productPrice: 0,
productCategory: ''
}
},
computed: {
// 用户相关计算属性
userDisplayName() {
return this.userName || this.userEmail
},
userAgeGroup() {
return this.userAge >= 18 ? '成人' : '未成年'
},
// 产品相关计算属性
productDiscount() {
return this.productPrice > 100 ? this.productPrice * 0.9 : this.productPrice
}
},
methods: {
// 用户相关方法
validateUser() { /* ... */ },
saveUser() { /* ... */ },
// 产品相关方法
validateProduct() { /* ... */ },
saveProduct() { /* ... */ }
},
watch: {
// 用户相关监听
userName() { /* ... */ },
userEmail() { /* ... */ },
// 产品相关监听
productName() { /* ... */ },
productPrice() { /* ... */ }
}
}
</script>
vue
<!-- 组合式 API - 相关逻辑可以聚合 -->
<script setup>
// 用户相关逻辑聚合在一起
const {
userName,
userEmail,
userAge,
userDisplayName,
userAgeGroup,
validateUser,
saveUser
} = useUser()
// 产品相关逻辑聚合在一起
const {
productName,
productPrice,
productCategory,
productDiscount,
validateProduct,
saveProduct
} = useProduct()
</script>
3. 逻辑复用对比
选项式 API 的 Mixin 方式(已不推荐)
javascript
// userMixin.js
export const userMixin = {
data() {
return {
userName: '',
userEmail: ''
}
},
methods: {
validateUser() {
return this.userName && this.userEmail
}
}
}
// ProductMixin.js
export const productMixin = {
data() {
return {
productName: '',
productPrice: 0
}
},
methods: {
validateProduct() {
return this.productName && this.productPrice > 0
}
}
}
// 组件中使用
export default {
mixins: [userMixin, productMixin],
// 命名冲突风险,数据来源不清晰
}
组合式 API 的 Composable 方式(推荐)
javascript
// composables/useUser.js
import { ref, computed } from 'vue'
export function useUser() {
const userName = ref('')
const userEmail = ref('')
const userDisplayName = computed(() => userName.value || userEmail.value)
const validateUser = () => {
return userName.value && userEmail.value
}
const saveUser = async () => {
// 保存逻辑
}
return {
userName,
userEmail,
userDisplayName,
validateUser,
saveUser
}
}
// composables/useProduct.js
import { ref, computed } from 'vue'
export function useProduct() {
const productName = ref('')
const productPrice = ref(0)
const productDiscount = computed(() =>
productPrice.value > 100 ? productPrice.value * 0.9 : productPrice.value
)
const validateProduct = () => {
return productName.value && productPrice.value > 0
}
return {
productName,
productPrice,
productDiscount,
validateProduct
}
}
// 组件中使用
<script setup>
import { useUser, useProduct } from '@/composables'
const { userName, userEmail, validateUser } = useUser()
const { productName, productPrice, validateProduct } = useProduct()
</script>
具体场景选择建议
1. 项目阶段选择
新手学习阶段
javascript
// 推荐:选项式 API
// 理由:概念清晰,学习曲线平缓
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
项目重构阶段
javascript
// 推荐:逐步迁移
// 理由:可以渐进式升级,降低风险
// 旧组件保持选项式 API
// 新组件使用组合式 API
// 两者可以共存
2. 团队背景选择
传统前端团队
javascript
// 推荐:选项式 API
// 团队成员更容易上手和维护
export default {
name: 'UserManagement',
data() {
return {
users: [],
loading: false,
searchKeyword: ''
}
},
computed: {
filteredUsers() {
return this.users.filter(user =>
user.name.includes(this.searchKeyword)
)
}
},
methods: {
async loadUsers() {
this.loading = true
try {
this.users = await userService.getUsers()
} finally {
this.loading = false
}
}
}
}
现代前端团队
javascript
// 推荐:组合式 API
// 更好的逻辑复用和维护性
// composables/useUserManagement.js
export function useUserManagement() {
const users = ref([])
const loading = ref(false)
const searchKeyword = ref('')
const filteredUsers = computed(() =>
users.value.filter(user =>
user.name.includes(searchKeyword.value)
)
)
const loadUsers = async () => {
loading.value = true
try {
users.value = await userService.getUsers()
} finally {
loading.value = false
}
}
return {
users,
loading,
searchKeyword,
filteredUsers,
loadUsers
}
}
// 组件中使用
<script setup>
import { useUserManagement } from '@/composables'
const { users, loading, searchKeyword, filteredUsers, loadUsers } = useUserManagement()
</script>
3. 功能复杂度选择
简单展示组件
vue
<!-- 推荐:任选其一,选项式 API 可能更简洁 -->
<script>
export default {
props: {
user: Object
},
computed: {
displayName() {
return this.user.nickname || this.user.name
}
}
}
</script>
复杂业务组件
vue
<!-- 推荐:组合式 API -->
<script setup>
// 多个业务逻辑域的聚合
const {
// 用户认证相关
isAuthenticated,
login,
logout
} = useAuth()
const {
// 表单相关
formData,
errors,
validate,
submit
} = useForm()
const {
// 数据获取相关
data,
loading,
error,
refresh
} = useApi('/api/users')
const {
// 权限相关
hasPermission,
checkPermission
} = usePermission()
// 组合逻辑
const canSubmit = computed(() =>
isAuthenticated.value &&
!loading.value &&
Object.keys(errors.value).length === 0
)
</script>
混合使用策略
Vue3 允许混合使用
vue
<script>
// 可以在选项式 API 中使用组合式 API
import { ref, computed } from 'vue'
import { useUser } from '@/composables'
export default {
data() {
return {
localCount: 0
}
},
setup() {
// 在 setup 中使用组合式 API
const { userName, userEmail } = useUser()
const doubleCount = computed(() => this.localCount * 2) // 注意这里的 this
return {
userName,
userEmail,
doubleCount
}
},
methods: {
increment() {
this.localCount++
}
}
}
</script>
推荐的混合使用场景
vue
<script>
// 在大型项目中,可以这样过渡
import { useUser, useProduct } from '@/composables'
export default {
// 保留选项式 API 的结构
data() {
return {
// 组件特有的简单状态
showDialog: false,
tabActive: 'user'
}
},
setup() {
// 复杂业务逻辑使用组合式 API
const userLogic = useUser()
const productLogic = useProduct()
return {
...userLogic,
...productLogic
}
},
methods: {
// 简单的事件处理
handleTabChange(tab) {
this.tabActive = tab
}
}
}
</script>
最终建议
选择标准总结:
场景 | 推荐 | 理由 |
---|---|---|
Vue 初学者 | 选项式 API | 学习曲线平缓,概念清晰 |
简单组件 | 任选其一 | 功能简单,两种方式差异不大 |
复杂组件 | 组合式 API | 逻辑聚合,易于维护 |
需要逻辑复用 | 组合式 API | Composable 比 Mixin 更好 |
大型项目 | 组合式 API | 更好的代码组织和维护性 |
团队统一 | 团队决定 | 保持项目一致性最重要 |
现有项目 | 渐进迁移 | 两种风格可以共存 |
实践建议:
- 新手团队:先用选项式 API 熟悉 Vue 概念
- 新项目 :推荐组合式 API +
<script setup>
- 老项目:可以保持现状,新功能用组合式 API
- 团队决策:统一风格比技术选型更重要
记住:没有绝对的好坏,只有适合与否。选择团队最能理解和维护的方式,就是最好的选择!