Vue 3 的组合式 API和传统选项式 API区别(vue2转vue3,两者差异)

选项式 API vs 组合式 API 深度对比

除了写法不同,选项式 API 和组合式 API 在设计理念、逻辑组织、类型支持、复用能力等方面都有本质区别。

1. 设计理念和思维模式的不同

选项式 API:基于"选项"的分类思维

javascript 复制代码
// 选项式 API - 按功能类型分类
export default {
  // 数据相关放一起
  data() {
    return {
      users: [],
      loading: false,
      searchQuery: ''
    }
  },
  
  // 计算属性放一起
  computed: {
    filteredUsers() {
      return this.users.filter(user => 
        user.name.includes(this.searchQuery)
      )
    }
  },
  
  // 方法放一起
  methods: {
    async fetchUsers() {
      this.loading = true
      this.users = await api.getUsers()
      this.loading = false
    },
    
    updateQuery(query) {
      this.searchQuery = query
    }
  },
  
  // 生命周期放一起
  mounted() {
    this.fetchUsers()
  }
}

组合式 API:基于"逻辑关注点"的聚合思维

vue 复制代码
<script setup>
import { ref, computed, onMounted } from 'vue'

// 用户搜索逻辑 - 相关的代码放在一起
const searchQuery = ref('')
const users = ref([])
const loading = ref(false)

const filteredUsers = computed(() => {
  return users.value.filter(user => 
    user.name.includes(searchQuery.value)
  )
})

const fetchUsers = async () => {
  loading.value = true
  users.value = await api.getUsers()
  loading.value = false
}

const updateQuery = (query) => {
  searchQuery.value = query
}

// 生命周期也跟相关逻辑放在一起
onMounted(() => {
  fetchUsers()
})

// 另一个独立的逻辑关注点可以放在下面
const otherFeature = () => {
  // 相关状态和方法都在一起
}
</script>

2. 逻辑组织和复用能力的本质区别

选项式 API 的逻辑复用问题

javascript 复制代码
// mixins/userMixin.js - 混入方式(容易冲突)
export default {
  data() {
    return {
      users: [],
      userLoading: false
    }
  },
  methods: {
    async fetchUsers() {
      this.userLoading = true
      this.users = await api.getUsers()
      this.userLoading = false
    }
  },
  mounted() {
    this.fetchUsers()
  }
}

// ComponentA.vue - 使用混入
export default {
  mixins: [userMixin],
  data() {
    return {
      // 可能跟混入中的 users 冲突
      products: [] 
    }
  },
  // 逻辑分散在不同选项中,难以追踪
}

组合式 API 的逻辑复用优势

javascript 复制代码
// composables/useUsers.js - 组合式函数
export function useUsers() {
  const users = ref([])
  const loading = ref(false)
  
  const fetchUsers = async () => {
    loading.value = true
    users.value = await api.getUsers()
    loading.value = false
  }
  
  onMounted(() => {
    fetchUsers()
  })
  
  return {
    users,
    loading,
    fetchUsers
  }
}

// ComponentA.vue - 使用组合式函数
<script setup>
import { useUsers } from '@/composables/useUsers'

const { users, loading, fetchUsers } = useUsers()

// 可以同时使用多个组合式函数,不会冲突
const { products, fetchProducts } = useProducts()
</script>

3. 响应式系统的使用差异

选项式 API 的响应式

javascript 复制代码
export default {
  data() {
    return {
      user: {
        name: 'John',
        profile: {
          age: 25
        }
      },
      items: [1, 2, 3]
    }
  },
  methods: {
    updateUser() {
      // Vue 2 中需要特殊处理数组和对象
      this.user.profile.age = 26 // 响应式更新
      this.items[0] = 999 // Vue 2 中不是响应式的!
      
      // Vue 2 的正确做法
      this.$set(this.items, 0, 999)
      this.items.splice(0, 1, 999)
    }
  }
}

组合式 API 的响应式

vue 复制代码
<script setup>
import { ref, reactive } from 'vue'

const user = reactive({
  name: 'John',
  profile: {
    age: 25
  }
})

const items = ref([1, 2, 3])

const updateUser = () => {
  user.profile.age = 26 // 响应式更新
  items.value[0] = 999 // 响应式更新
  
  // 更灵活的响应式操作
  const newItem = ref(100)
  items.value.push(newItem.value)
}
</script>

4. TypeScript 支持程度的巨大差异

选项式 API 的 TypeScript 支持有限

typescript 复制代码
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      count: 0, // 类型可以推断为 number
      user: null as User | null // 需要类型断言
    }
  },
  
  computed: {
    // 计算属性的类型声明比较麻烦
    doubleCount(): number {
      return this.count * 2
    }
  },
  
  methods: {
    // 方法参数和返回值的类型声明
    updateUser(user: User): void {
      this.user = user
    }
  },
  
  // 生命周期钩子没有很好的类型提示
  mounted() {
    // this.$ 上的属性类型支持有限
  }
})

组合式 API 的完整 TypeScript 支持

vue 复制代码
<script setup lang="ts">
import { ref, computed } from 'vue'

interface User {
  id: number
  name: string
  email: string
}

// 完整的类型推断
const count = ref(0) // 自动推断为 Ref<number>
const user = ref<User | null>(null) // 明确的类型

// 计算属性的完整类型支持
const doubleCount = computed(() => count.value * 2) // 自动推断为 ComputedRef<number>

// 函数的完整类型支持
const updateUser = (newUser: User) => {
  user.value = newUser
}

// 自动补全和类型检查
user.value?.name // 完整的智能提示
</script>

5. 代码组织和维护性的对比

复杂组件在选项式 API 中的问题

javascript 复制代码
export default {
  data() {
    return {
      // 多个功能的变量混在一起
      users: [],
      products: [], 
      orders: [],
      userLoading: false,
      productLoading: false,
      orderLoading: false,
      searchQuery: '',
      filterStatus: '',
      pagination: { page: 1, limit: 20 }
    }
  },
  
  computed: {
    // 多个功能的计算属性混在一起
    filteredUsers() { /* ... */ },
    filteredProducts() { /* ... */ },
    filteredOrders() { /* ... */ },
    paginatedUsers() { /* ... */ },
    paginatedProducts() { /* ... */ }
  },
  
  methods: {
    // 多个功能的方法混在一起
    fetchUsers() { /* ... */ },
    fetchProducts() { /* ... */ },
    fetchOrders() { /* ... */ },
    searchUsers() { /* ... */ },
    searchProducts() { /* ... */ }
  },
  
  mounted() {
    // 多个功能的初始化混在一起
    this.fetchUsers()
    this.fetchProducts()
    this.fetchOrders()
  }
}

组合式 API 的逻辑分离优势

vue 复制代码
<script setup>
import { useUsers } from './composables/useUsers'
import { useProducts } from './composables/useProducts'
import { useOrders } from './composables/useOrders'

// 每个功能独立,清晰分离
//从 `useUsers` 函数的返回值中提取出 `users`、`userLoading`、`filteredUsers`、`fetchUsers`、`searchUsers` 这些属性
const {
  users,
  userLoading,
  filteredUsers,
  fetchUsers,
  searchUsers
} = useUsers()

const {
  products,
  productLoading, 
  filteredProducts,
  fetchProducts,
  searchProducts
} = useProducts()

const {
  orders,
  orderLoading,
  filteredOrders, 
  fetchOrders
} = useOrders()

// 初始化各个功能
onMounted(() => {
  fetchUsers()
  fetchProducts()
  fetchOrders()
})
</script>

6. 学习曲线和心智模型

选项式 API 的学习曲线

javascript 复制代码
// 相对平缓,符合传统 OOP 思维
export default {
  props: ['message'],     // 输入
  data() {               // 状态
    return { count: 0 }
  },
  computed: {            // 派生状态
    double() { return this.count * 2 }
  },
  methods: {             // 方法
    increment() { this.count++ }
  },
  watch: {               // 副作用
    count(newVal) { console.log(newVal) }
  },
  mounted() {            // 生命周期
    console.log('组件挂载')
  }
}

组合式 API 的学习曲线

vue 复制代码
<script setup>
import { ref, computed, watch, onMounted } from 'vue'

// 需要理解响应式基础
const count = ref(0)
const double = computed(() => count.value * 2)

// 需要理解作用域和闭包
const increment = () => {
  count.value++
}

// 需要理解生命周期注册
onMounted(() => {
  console.log('组件挂载')
})

// 需要理解侦听器机制
watch(count, (newVal) => {
  console.log(newVal)
})
</script>

7. 性能优化的差异

选项式 API 的性能优化

javascript 复制代码
export default {
  data() {
    return {
      largeList: [] // 整个组件重新渲染
    }
  },
  methods: {
    updateItem(index, newValue) {
      // 需要特殊优化手段
      this.$set(this.largeList, index, newValue)
    }
  }
}

组合式 API 的性能优化

vue 复制代码
<script setup>
import { ref, shallowRef, markRaw } from 'vue'

// 更细粒度的响应式控制
const largeList = shallowRef([]) // 浅层响应式
const heavyObject = markRaw({     // 非响应式
  // 大型静态数据
})

// 更精确的更新控制
const updateItem = (index, newValue) => {
  const newList = [...largeList.value]
  newList[index] = newValue
  largeList.value = newList
}
</script>

8. 与第三方库集成的差异

选项式 API 的集成

javascript 复制代码
import { mapState, mapActions } from 'vuex'

export default {
  computed: {
    ...mapState(['user', 'settings']),
    localComputed() { /* ... */ }
  },
  methods: {
    ...mapActions(['login', 'logout']),
    localMethod() { /* ... */ }
  }
  // 混合了 Vuex 和本地逻辑,难以区分
}

组合式 API 的集成

vue 复制代码
<script setup>
import { useStore } from 'vuex'
import { useLocalLogic } from './composables/useLocalLogic'

// 清晰的分离
const store = useStore()
const user = computed(() => store.state.user)
const login = () => store.dispatch('login')

// 本地逻辑
const { localData, localMethod } = useLocalLogic()
</script>

9. 实际项目中的选择建议

适合选项式 API 的场景

javascript 复制代码
// 1. 简单的展示组件
export default {
  props: ['title', 'content'],
  template: `
    <div class="card">
      <h3>{{ title }}</h3>
      <p>{{ content }}</p>
    </div>
  `
}

// 2. 迁移 Vue 2 项目
// 3. 团队对选项式 API 更熟悉
// 4. 不需要复杂逻辑复用

适合组合式 API 的场景

vue 复制代码
<script setup>
// 1. 复杂的业务组件
const { 
  data, 
  pagination, 
  filters, 
  search, 
  loadData 
} = useDataTable()

const { 
  form, 
  validation, 
  submit, 
  reset 
} = useFormHandler()

// 2. 需要逻辑复用的场景
// 3. TypeScript 项目
// 4. 大型应用开发
</script>

总结

特性 选项式 API 组合式 API
设计理念 按选项分类 按逻辑关注点聚合
逻辑复用 Mixins(有冲突风险) 组合式函数(无冲突)
TypeScript 有限支持 完整支持
代码组织 功能分散在不同选项 相关逻辑集中
响应式 自动处理,但有限制 显式声明,更灵活
学习曲线 平缓 较陡峭
性能优化 组件级别 更细粒度控制
适用场景 简单组件、Vue 2 迁移 复杂组件、大型项目

核心区别: 选项式 API 是"怎么做",组合式 API 是"做什么"。选择哪种取决于项目复杂度、团队习惯和具体需求。

相关推荐
LuckySusu3 小时前
【vue篇】Vue 项目中的静态资源管理:assets vs static 终极指南
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue.delete vs delete:数组删除的“陷阱”与正确姿势
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue 模板编译原理:从 Template 到 DOM 的翻译官
前端·vue.js
小菜摸鱼3 小时前
Node.js + vue3 大文件-切片上传全流程(视频文件)
前端·node.js
LuckySusu3 小时前
【vue篇】Vue 2 响应式“盲区”破解:如何监听对象/数组属性变化
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue Mixin:可复用功能的“乐高积木”
前端·vue.js
洋不写bug3 小时前
前端环境搭建,保姆式教学
前端
需要兼职养活自己3 小时前
react高阶组件
前端·react.js
TechFrank3 小时前
Shadcn/ui 重磅更新:7 个实用新组件深度解析与实战指南
前端