Options API 和 Composition API 有什么区别?Vue 组件的 data 为什么必须是函数?
Options API 按 data、methods、computed 这些选项组织代码,通过 this 访问组件实例;Composition API 用导入的函数在 setup 里组织逻辑,同一功能的代码可以写在一起,也能抽成组合式函数复用,对 TypeScript 更友好。两者底层是同一套系统,Options API 就建在 Composition API 之上。组件的 data 写成函数,是为了每个实例都拿到独立的数据对象。
简答
写一个搜索页,有关键词、搜索结果、加载状态,还有分页。用 Options API 写,关键词放在
data,搜索函数放在methods,“是否有结果”放在computed,监听关键词变化放在watch,一个“搜索”功能被拆散到四个地方。Composition API 则在setup或<script setup>里,用ref、computed、watch这些导入的函数,把搜索相关的代码写在一起。这带来两个好处。一是功能复杂以后,同一件事的代码集中在一处,读起来不用上下跳;二是可以把一段逻辑原样抽成一个函数,比如
useSearch(),在多个组件里复用,替代 Vue 2 里容易命名冲突、来源不清的 mixin。它基于普通的变量和函数,TypeScript 也能更好地推导类型。Options API 也有优点,它通过
this访问组件实例,代码按选项分组,结构固定,对初学者更友好。Vue 官方文档说两者是同一套底层系统的不同接口,Options API 实际上是在 Composition API 之上实现的,可以按项目情况选择。组件的
data必须写成返回对象的函数,是因为同一个组件会创建多个实例。如果data直接是一个对象,所有实例引用的就是同一个对象,一个改了全都变;写成函数后,每创建一个实例调用一次,各自拿到新对象。Composition API 里这个问题不存在,setup本来就是每个实例调用一次。
同一个功能两种写法
<!-- Options API -->
<script>
export default {
data() {
return { keyword: '', results: [] };
},
computed: {
empty() {
return this.results.length === 0;
},
},
watch: {
keyword(value) {
this.search(value);
},
},
methods: {
async search(value) {
this.results = await fetchResults(value);
},
},
};
</script>
<!-- Composition API -->
<script setup>
import { computed, ref, watch } from 'vue';
const keyword = ref('');
const results = ref([]);
const empty = computed(() => results.value.length === 0);
watch(keyword, async (value) => {
results.value = await fetchResults(value);
});
</script>
两段代码功能一样。第一段里,“搜索”分布在 data、computed、watch、methods 四个选项中;第二段里,所有搜索相关的代码挨在一起。现在只有一个功能还看不出差别,等组件里还有分页、筛选、导出,Options API 的每个选项都会混进几个功能的代码。
要复用时,把第二段里的几行剪出来放进 function useSearch() { ...; return { keyword, results, empty }; },任何组件调用它就能拿到一套独立的状态。组合式函数怎样设计,见《组合式函数和 mixin 相比有什么维护优势?》。
data 是对象会出什么问题
const shared = { count: 0 };
const instanceA = shared;
const instanceB = shared;
instanceA.count += 1;
console.log(instanceB.count); // 1
这就是 data 写成对象时的情况:两个组件实例拿到的是同一个对象的引用,一个改了,另一个也看到了。写成 data() { return { count: 0 }; },Vue 每创建一个实例就调用一次这个函数,得到一个新对象,实例之间就互不影响。
Vue 2 中如果组件的 data 不是函数,会在开发环境给出警告。根实例 new Vue({ data: {...} }) 可以是对象,因为它只有一个。Vue 3 里 data 选项只接受函数。
常见追问
Composition API 里为什么没有 this?
setup在组件实例完全创建之前执行,那时还没有可用的this。状态都是setup里的局部变量,通过闭包被模板和函数访问,所以也不需要this。
Composition API 会不会让代码更乱?
如果把所有代码都堆在
setup里不加组织,确实会更乱。好的做法是按功能分段,把复杂逻辑抽成组合式函数,组件里只负责把它们组合起来。Options API 的结构是框架强制的,Composition API 的结构要靠开发者自己维护。
两种写法能混用吗?
能。组件可以同时写
setup()和其他选项,setup返回的值可以在选项里通过this访问。但同一个项目最好统一风格,新代码一般用 Composition API。