Vue3 创建单文件组件(SFC)(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观


2. 单文件组件(SFC)的三要素

在 Vue3 的世界中,单文件组件(Single-File Component, SFC)是构建应用的核心单元。它将模板(Template)、脚本(Script)和样式(Style)封装在以 .vue 为后缀的文件中,如同一个“乐高积木”,让开发者可以灵活组合功能模块。

2.1 基础语法结构

一个典型的 SFC 文件包含以下部分:

<template>
  <!-- 用户界面的 HTML 结构 -->
</template>

<script setup>
// 逻辑代码(如数据、方法、生命周期)
</script>

<style scoped>
/* 组件专属样式 */
</style>

比喻:可以将 SFC 想象为一个“三明治”——模板是面包,脚本是夹心,样式是酱料,三者紧密结合形成完整的功能单元。

2.2 模板语法:构建用户界面

模板是 SFC 的“视觉层”,通过 HTML 和 Vue 的指令(如 v-ifv-for)实现动态渲染。例如:

<template>
  <div class="counter">
    <p>当前计数:{{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

关键点

  • {{ }} 是数据绑定语法,用于显示响应式数据;
  • @click 是事件监听指令,绑定到 increment 方法。

2.3 脚本:驱动逻辑的“心脏”

<script setup> 中定义组件的逻辑。Vue3 推荐使用 组合式 API(Composition API),通过 refreactive 等函数管理状态:

<script setup>
import { ref } from 'vue';

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

组合式 API 的优势

  • 将逻辑按功能组织(如 setup() 函数),而非按选项分割;
  • 通过 ref/reactive 明确区分响应式数据和普通变量。

2.4 样式:局部与全局的平衡

通过 <style> 标签定义样式,默认是全局生效的。添加 scoped 属性后,样式仅作用于当前组件:

<style scoped>
.counter {
  padding: 20px;
  border: 1px solid #ccc;
}
</style>

作用域样式的意义:避免不同组件样式冲突,如同为每个组件穿上“隐形斗篷”。


3. 从零开始创建第一个 SFC

3.1 项目环境准备

确保已安装 Node.js 和 Vue CLI:

npm install -g @vue/cli
vue create my-project
cd my-project
npm run serve

提示:在创建项目时,选择 Vue3 模板。

3.2 实战案例:计数器组件

src/components 目录下新建 Counter.vue,代码如下:

<template>
  <div class="counter">
    <h3>计数器</h3>
    <p>当前值:{{ count }}</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const count = ref(0);

const increment = () => {
  count.value++;
};

const decrement = () => {
  count.value--;
};
</script>

<style scoped>
.counter {
  border: 1px solid #42b883;
  padding: 20px;
  margin: 10px;
  text-align: center;
}
</style>

运行效果

  • 点击按钮可增减数值;
  • 样式仅应用于当前组件。

4. 组合式 API 的深度探索

4.1 setup() 函数与响应式数据

在 Vue3 中,setup() 是组件逻辑的入口函数,默认通过 <script setup> 简化语法。例如:

<script setup>
import { ref } from 'vue';

const count = ref(0);
const doubleCount = computed(() => count.value * 2);
</script>

关键概念

  • ref:包装基本类型(如数字、字符串)为响应式对象;
  • reactive:用于复杂对象(如数组、对象);
  • computed:计算属性,依赖响应式数据自动更新。

4.2 生命周期钩子与依赖注入

Vue3 通过 onMountedonUnmounted 等函数替代 Vue2 的选项式钩子:

onMounted(() => {
  console.log('组件已挂载');
});

provide('theme', 'dark'); // 向子组件提供数据

5. 组件间的通信:父子协作

5.1 父组件向子组件传递数据(props)

在父组件中定义 props,并通过 <child-component :prop-name="value" /> 传递:

<!-- ParentComponent.vue -->
<template>
  <Counter :initial-value="startingValue" />
</template>

<script setup>
const startingValue = ref(10);
</script>

子组件通过 defineProps 接收:

<!-- Counter.vue -->
<script setup>
const props = defineProps({
  initialValue: {
    type: Number,
    default: 0,
  },
});
const count = ref(props.initialValue);
</script>

5.2 子组件向父组件发送事件

子组件通过 $emit 触发事件,父组件监听并处理:

<!-- Counter.vue -->
<button @click="$emit('update-count', count.value)">同步父组件</button>

父组件监听事件:

<Counter @update-count="handleUpdate" />
<script setup>
const handleUpdate = (newValue) => {
  console.log('新值:', newValue);
};
</script>

6. 实战案例:父子组件协同的 Todo List

6.1 父组件管理全局状态

<!-- App.vue -->
<template>
  <div>
    <TodoInput @add-todo="addTodo" />
    <TodoList :todos="todos" @remove-todo="removeTodo" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import TodoInput from './components/TodoInput.vue';
import TodoList from './components/TodoList.vue';

const todos = ref([]);

const addTodo = (text) => {
  todos.value.push({ id: Date.now(), text });
};

const removeTodo = (id) => {
  todos.value = todos.value.filter(todo => todo.id !== id);
};
</script>

6.2 子组件实现输入与列表展示

<!-- TodoInput.vue -->
<template>
  <input
    v-model="inputText"
    @keydown.enter="emitAdd"
    placeholder="输入待办事项..."
  />
</template>

<script setup>
import { ref } from 'vue';

const inputText = ref('');
const emit = defineEmits(['add-todo']);

const emitAdd = () => {
  if (inputText.value.trim()) {
    emit('add-todo', inputText.value);
    inputText.value = '';
  }
};
</script>
<!-- TodoList.vue -->
<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      {{ todo.text }}
      <button @click="$emit('remove-todo', todo.id)">删除</button>
    </li>
  </ul>
</template>

<script setup>
defineProps({
  todos: {
    type: Array,
    required: true,
  },
});
</script>

7. 高级技巧与优化

7.1 使用 TypeScript 增强类型检查

<script setup> 中添加 lang="ts",并定义类型:

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

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

const user = ref<User | null>(null);
</script>

7.2 组件复用与抽象组件

通过 defineComponent 创建可复用的组件:

// MyComponent.ts
import { defineComponent } from 'vue';

export default defineComponent({
  props: {
    title: String,
  },
  setup(props) {
    return () => <h1>{props.title}</h1>;
  },
});

8. 总结与展望

通过本文的讲解,读者已掌握 Vue3 创建单文件组件(SFC)的核心方法,包括模板、脚本、样式的协同工作,以及组合式 API 的优势。从基础的计数器到复杂的 Todo List,实战案例展示了组件通信与状态管理的实现路径。

下一步建议

  1. 阅读 Vue3 官方文档,深入理解响应式系统;
  2. 尝试用 SFC 构建一个完整的项目(如博客系统或电商页面);
  3. 探索 Vue3 的进阶特性,如 Suspense 组件、Teleport 等。

通过持续实践,开发者将能灵活运用 Vue3 的单文件组件(SFC),构建高效、可维护的前端应用。

最新发布