Vue3 <script setup> 中不需要使用 defineComponent

Vue3的<script setup>语法相比传统Options API写法更加简洁高效。


它通过编译宏如definePropsdefineEmits等替代了defineComponent,减少了样板代码,同时提供更好的TypeScript支持。


<script setup>中,响应式数据、方法、生命周期等都可以直接编写,代码组织更接近Composition API逻辑。


虽然仍支持与Options API混用,但推荐使用纯<script setup>写法,这是Vue3推荐的开发方式,能自动编译为组件选项,使开发更加高效简洁。


<script setup> 中不需要使用 defineComponent


传统写法 vs <script setup>

1. 传统 Options API 写法

javascript 复制代码
<!-- 需要 defineComponent -->
<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'MyComponent',
  props: {
    title: String
  },
  data() {
    return {
      count: 0
    };
  },
  methods: {
    increment() {
      this.count++;
    }
  }
});
</script>

2. 使用 <script setup>(推荐)

TypeScript 复制代码
<!-- 不需要 defineComponent -->
<script setup lang="ts">
import { ref } from 'vue';

// Props 定义
const props = defineProps<{
  title: string;
}>();

// 响应式数据
const count = ref(0);

// 方法
const increment = () => {
  count.value++;
};
</script>

<script setup> 中的替代 API

1. 定义 Props

TypeScript 复制代码
<script setup lang="ts">
// 方式1:使用泛型(推荐)
defineProps<{
  title: string;
  count?: number;
  items: string[];
}>();

// 方式2:使用运行时声明
defineProps({
  title: String,
  count: {
    type: Number,
    default: 0
  }
});

// 方式3:混合使用(Vue 3.3+)
interface Props {
  title: string;
  count?: number;
}

defineProps<Props>();
</script>

2. 定义 Emits

TypeScript 复制代码
<script setup lang="ts">
// 方式1:使用泛型
const emit = defineEmits<{
  (e: 'update', value: string): void;
  (e: 'submit', payload: { id: number }): void;
}>();

// 方式2:数组形式
const emit = defineEmits(['update', 'submit']);

// 方式3:对象形式(带验证)
const emit = defineEmits({
  update: (value: string) => typeof value === 'string',
  submit: (payload: { id: number }) => payload.id > 0
});
</script>

3. 定义 Slots 和 Attrs

TypeScript 复制代码
<script setup lang="ts">
import { useSlots, useAttrs } from 'vue';

const slots = useSlots();
const attrs = useAttrs();
</script>

4. 定义 Expose

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

const inputRef = ref<HTMLInputElement>();

// 暴露给父组件的方法
defineExpose({
  focus: () => inputRef.value?.focus(),
  clear: () => {
    if (inputRef.value) inputRef.value.value = '';
  }
});
</script>

为什么不需要 defineComponent

<script setup> 的优势:

  1. 自动编译<script setup> 会在编译时转换为正常的组件选项

  2. 更简洁:减少样板代码

  3. 更好的类型推断:TypeScript 支持更好

  4. 更直观:代码组织更接近 Composition API 的逻辑


编译对比:

javascript 复制代码
// <script setup> 代码
<script setup lang="ts">
const count = ref(0);
</script>

// 编译后的结果 ≈
<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const count = ref(0);
    return { count };
  }
});
</script>

特殊情况处理

1. 如果需要组件名(用于调试或递归组件)

TypeScript 复制代码
<!-- 方式1:使用两个 script 标签 -->
<script lang="ts">
export default {
  name: 'MyComponent'
};
</script>

<script setup lang="ts">
// Composition API 代码
const count = ref(0);
</script>

<!-- 方式2:使用插件(Vue 3.3+) -->
<script setup lang="ts">
defineOptions({
  name: 'MyComponent',
  inheritAttrs: false
});
</script>

2. 混合使用 Options API

虽然不推荐,但如果需要:

TypeScript 复制代码
<script setup lang="ts">
// Composition API 部分
const count = ref(0);
</script>

<script lang="ts">
import { defineComponent } from 'vue';

// Options API 部分
export default defineComponent({
  name: 'MyComponent',
  // 这里可以添加 computed, watch 等
  computed: {
    doubled() {
      // 注意:无法直接访问 setup 中的变量
      return 0;
    }
  }
});
</script>

完整示例

TypeScript 复制代码
<template>
  <div>
    <h1>{{ title }}</h1>
    <p>计数: {{ count }}</p>
    <button @click="increment">增加</button>
    <button @click="handleSubmit">提交</button>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';

// Props
interface Props {
  title: string;
  initialCount?: number;
}

const props = withDefaults(defineProps<Props>(), {
  initialCount: 0
});

// Emits
const emit = defineEmits<{
  (e: 'update:count', value: number): void;
  (e: 'submit', payload: { count: number }): void;
}>();

// 状态
const count = ref(props.initialCount);

// 方法
const increment = () => {
  count.value++;
  emit('update:count', count.value);
};

const handleSubmit = () => {
  emit('submit', { count: count.value });
};

// 生命周期
onMounted(() => {
  console.log('组件已挂载');
});

// 暴露给父组件
defineExpose({
  reset: () => {
    count.value = 0;
  }
});
</script>

<style scoped>
/* 样式 */
</style>

总结

<script setup> 中:

  • 不需要 defineComponent

  • ✅ 使用 definePropsdefineEmitsdefineExpose 等编译宏

  • ✅ 代码更简洁,类型支持更好

  • ✅ 是 Vue 3 的推荐写法


只有在使用传统 Options API 写法时才需要 defineComponent,而 <script setup> 是 Composition API 的语法糖,会自动处理这些。

相关推荐
xkxnq2 小时前
第二阶段:Vue 组件化开发(第 21天)
前端·javascript·vue.js
内存不泄露2 小时前
基于 Spring Boot 的医院预约挂号系统(全端协同)设计与实现
java·vue.js·spring boot·python·flask
0_13 小时前
封装了一个vue版本 Pag组件
前端·javascript·vue.js
Code知行合壹3 小时前
Vue.js进阶
前端·javascript·vue.js
千寻girling3 小时前
Vue.js 前端开发实战 ( 电子版 ) —— 黑马
前端·javascript·vue.js·b树·决策树·随机森林·最小二乘法
OpenTiny社区4 小时前
TinyPro v1.4 空降:Spring Boot 集成,后端兄弟也能愉快写前端!
前端·javascript·vue.js
R-sz4 小时前
UE5像素流与Vue通信
前端·vue.js·ue5
古迪红尘4 小时前
el-tree 采用懒加载方式,怎么初始化就显示根节点和下级节点
前端·javascript·vue.js
Aotman_4 小时前
Vue el-table 字段自定义排序(进阶)
前端·javascript·vue.js·elementui·前端框架·ecmascript