Vue3 创建单文件组件(SFC)(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 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-if
、v-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),通过 ref
、reactive
等函数管理状态:
<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 通过 onMounted
、onUnmounted
等函数替代 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,实战案例展示了组件通信与状态管理的实现路径。
下一步建议:
- 阅读 Vue3 官方文档,深入理解响应式系统;
- 尝试用 SFC 构建一个完整的项目(如博客系统或电商页面);
- 探索 Vue3 的进阶特性,如 Suspense 组件、Teleport 等。
通过持续实践,开发者将能灵活运用 Vue3 的单文件组件(SFC),构建高效、可维护的前端应用。