Vue3.0 的 Composition API 与 Vue2.x 的 Options API 有什么不同?
问题解析
这是 Vue3 最核心的变化之一。面试考察这个问题,是想要了解候选人对 Vue3 新特性的理解,以及是否能够掌握更高效的代码组织方式。
核心概念
什么是 Options API
Options API(选项式 API)是 Vue2 的传统写法,通过 data、methods、computed、watch 等选项来组织代码:
export default {
data() {
return {
count: 0,
message: ''
}
},
computed: {
doubleCount() {
return this.count * 2;
}
},
methods: {
increment() {
this.count++;
}
},
watch: {
count(newVal) {
console.log('count changed:', newVal);
}
}
}
什么是 Composition API
Composition API(组合式 API)是 Vue3 引入的新方式,通过 setup 函数和相关 API 来组织代码:
import { ref, computed, watch } from 'vue';
export default {
setup() {
const count = ref(0);
const message = ref('');
const doubleCount = computed(() => count.value * 2);
const increment = () => {
count.value++;
};
watch(count, (newVal) => {
console.log('count changed:', newVal);
});
return {
count,
message,
doubleCount,
increment
};
}
}
详细解答
1. 逻辑组织方式对比
Options API 的问题:
┌─────────────────────────────────────────┐
│ 大型组件中的代码分布 │
├─────────────────────────────────────────┤
│ data: [属性A, 属性B, 属性C...] │
│ methods: [方法A, 方法B, 方法C...] │
│ computed: [计算A, 计算B, 计算C...] │
│ watch: [监听A, 监听B, 监听C...] │
└─────────────────────────────────────────┘
↓ 按选项类型分散
问题:一个功能的代码分散在不同选项中
Composition API 的优势:
┌─────────────────────────────────────────┐
│ 功能A: [data + methods + computed] │
│ 功能B: [data + methods + computed] │
│ 功能C: [data + methods + computed] │
└─────────────────────────────────────────┘
↓ 按功能逻辑聚合
优势:高内聚、低耦合
2. 代码复用方式对比
Vue2 - Mixin 的问题:
// mouseMixin.js
export const mouseMixin = {
data() {
return { x: 0, y: 0 };
},
mounted() {
window.addEventListener('mousemove', this.update);
},
methods: {
update(e) {
this.x = e.pageX;
this.y = e.pageY;
}
}
};
// 组件中使用
export default {
mixins: [mouseMixin, otherMixin, anotherMixin],
// 问题1: 命名冲突(多个 mixin 可能有相同的属性名)
// 问题2: 数据来源不清晰(这个 x 来自哪个 mixin?)
}
Vue3 - Composable 函数:
// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue';
export function useMouse() {
const x = ref(0);
const y = ref(0);
const update = (e) => {
x.value = e.pageX;
y.value = e.pageY;
};
onMounted(() => window.addEventListener('mousemove', update));
onUnmounted(() => window.removeEventListener('mousemove', update));
return { x, y };
}
// 组件中使用
import { useMouse } from './useMouse';
import { useOther } from './useOther';
export default {
setup() {
const { x, y } = useMouse(); // 来源清晰
const { data } = useOther(); // 来源清晰
return { x, y, data };
}
}
3. TypeScript 支持
Options API 的类型问题:
export default {
data() {
return {
count: 0 // 类型推断有限
};
},
methods: {
increment() {
this.count++; // this 的类型推断不完整
}
}
}
Composition API 的类型优势:
import { ref, computed } from 'vue';
export default {
setup() {
const count = ref<number>(0); // 完整的类型推断
const double = computed<number>(() => count.value * 2);
const increment = (): void => {
count.value++;
};
return { count, double, increment };
}
}
深入理解
1. 为什么 Composition API 更易维护
// 实际案例:一个包含搜索、分页、排序的表格组件
// Options API 写法 - 逻辑分散
export default {
data() {
return {
searchQuery: '', // 搜索相关
searchResults: [],
currentPage: 1, // 分页相关
pageSize: 10,
total: 0,
sortField: '', // 排序相关
sortOrder: 'asc'
};
},
methods: {
handleSearch() { /* ... */ }, // 搜索方法
handlePageChange() { /* ... */ }, // 分页方法
handleSort() { /* ... */ } // 排序方法
}
// 修改搜索功能需要在 data、methods、computed 之间跳转
}
// Composition API 写法 - 逻辑聚合
export default {
setup() {
// 搜索功能内聚
const { searchQuery, searchResults, handleSearch } = useSearch();
// 分页功能内聚
const { currentPage, pageSize, total, handlePageChange } = usePagination();
// 排序功能内聚
const { sortField, sortOrder, handleSort } = useSorting();
return {
searchQuery, searchResults, handleSearch,
currentPage, pageSize, total, handlePageChange,
sortField, sortOrder, handleSort
};
}
}
2. Setup 语法糖(更简洁的写法)
<script setup>
import { ref, computed } from 'vue';
// 直接定义变量和方法
const count = ref(0);
const double = computed(() => count.value * 2);
const increment = () => count.value++;
// 无需 return,自动暴露给模板
</script>
3. 响应式系统的统一
// Vue2: 响应式数据定义在 data 中,methods 中的 this 指向组件实例
export default {
data() {
return { count: 0 };
},
methods: {
increment() {
this.count++; // 依赖 this
}
}
}
// Vue3: 统一的响应式 API,不依赖 this
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increment = () => count.value++; // 不依赖 this
return { count, increment };
}
}
最佳实践
1. 何时使用 Options API
// 小型组件、简单逻辑时,Options API 仍然简洁
export default {
data() {
return {
isShow: false,
title: 'Hello'
};
},
methods: {
toggle() {
this.isShow = !this.isShow;
}
}
}
2. Composable 函数的命名规范
// 使用 use 前缀
useMouse.js // 鼠标位置
useFetch.js // 数据请求
useLocalStorage.js // 本地存储
useDebounce.js // 防抖
// 清晰的功能命名
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
const increment = () => count.value++;
const decrement = () => count.value--;
return {
count,
increment,
decrement
};
}
3. 代码组织原则
export default {
setup() {
// 1. 响应式数据定义
const state = reactive({
list: [],
loading: false
});
// 2. 计算属性
const total = computed(() => state.list.length);
// 3. 方法定义
const fetchData = async () => {
state.loading = true;
// ...
state.loading = false;
};
// 4. 生命周期钩子
onMounted(fetchData);
// 5. 返回给模板使用
return {
...toRefs(state),
total,
fetchData
};
}
}
4. 迁移策略
// Vue3 中仍然支持 Options API
// 可以渐进式迁移:
// 1. 新功能使用 Composition API
// 2. 旧组件保持 Options API
// 3. 逐步重构核心组件
// 混合使用(不推荐长期使用)
export default {
data() {
return { legacyData: 'old' };
},
setup() {
const newData = ref('new');
return { newData };
}
}
面试要点
-
能够清晰对比两种 API 的组织方式差异
- Options API:按选项类型组织
- Composition API:按功能逻辑组织
-
理解 Mixin 的缺陷和 Composable 的优势
- 命名冲突、数据来源不清晰 vs 来源清晰、可组合
-
知道 Composition API 更适合的场景
- 大型组件、复杂逻辑、需要复用逻辑、TypeScript 项目
-
了解
<script setup>语法糖- 更简洁的写法,自动暴露变量
-
理解响应式 API(ref/reactive)
- ref 用于基本类型和需要替换的对象
- reactive 用于对象类型
核心结论:
- Composition API 解决了 Options API 在大型组件中的逻辑分散问题
- 提供了更好的代码复用机制(Composable)
- 拥有更好的 TypeScript 类型推断
- 不意味着完全抛弃 Options API,小型组件仍可继续使用