Vue 3 单文件组件完全指南
本文档从零开始讲解 Vue 3 的单文件组件(SFC,Single File Component):什么是 .vue 文件、 / / 各自写什么、多块 script 与 style、scoped 与深度选择器、预处理器与 src 引用等,配有大量示例,适合新手系统学习。
一、什么是单文件组件?
1.1 概念
单文件组件就是一个 .vue 文件对应一个 Vue 组件。
这个文件里把结构(HTML)、逻辑(JavaScript/TypeScript)、样式(CSS) 写在一起,用三块标签区分:
- :组件的模板(HTML + Vue 模板语法)。
- :组件的逻辑(数据、方法、生命周期等)。
- :组件的样式(只影响当前组件或全局,可加 scoped)。
这样一个功能就在一个文件里,便于维护和复用。
1.2 长什么样?
一个最小的单文件组件示例:
<!-- HelloWorld.vue -->
<template>
<p>你好,{{ name }}!</p>
</template>
<script setup>
import { ref } from 'vue'
const name = ref('Vue 3')
</script>
<style scoped>
p {
color: blue;
}
</style>
- :只能有一个根级“容器”(Vue 3 可以是多个根节点,见下文)。
- :用组合式 API,顶层变量自动暴露给模板。
- :只影响本组件内的元素,不污染其它组件。
下面分别细说这三块。
二、:模板
2.1 只能有一个根,或多个根(Vue 3)
Vue 2: 里必须有且只有一个根元素,多写几个并列的会报错。
Vue 3:支持多个根节点(Fragment),例如:
<template>
<header>头部</header>
<main>主体</main>
<footer>底部</footer>
</template>
若只有一个根,通常包在一个 不能写多个顶层元素之外的非 HTML、非 Vue 的语法(例如不能直接写 )。 若模板很长,可以放到单独文件里,用 src 引用: 一般不常用,多数情况直接写在 里即可。 写法一:(推荐) 写法二:选项式 (export default) 新手若从组合式 API 学起,用 即可。 可以同时存在: 若项目配了 TypeScript,可给 script 加 lang=”ts”: 不加 scoped 时, 里的选择器是全局的,会影响整个项目里匹配到的元素,容易误伤其它组件。 加上 scoped 后,Vue 会给当前组件的根元素及其子元素加一个唯一属性(如 data-v-xxxxx),选择器会变成“只匹配带这个属性的元素”,从而只作用于本组件。 可以写多个 ,有的 scoped,有的不写(全局): 若要在父组件的 scoped 样式里,选中子组件内部的元素,要用深度选择器。 Vue 3.2+ 支持在 里用 v-bind(变量),把 script 里的响应式数据绑到 CSS 上: 若项目装了 sass 或 less,可以给 style 加 lang=”scss” 或 lang=”less”: 需要先安装对应依赖(如 sass、less)。 样式也可以放到单独文件,用 src 引入: 在父组件里 import 这个 .vue 文件,在 里 import 后,模板里可直接当标签用( 会自动暴露 import 的组件): 不能写多个不包在一起的顶层元素再在外面包一层别的(逻辑上仍是“一个根”即可)。Vue 3 多根时,每个根是平级的。 子组件根元素会带父的 data-v-xxx,所以父的 scoped 能选中子组件根;子组件内部的类要用 :deep() 才能从父组件样式里命中。 写 scoped 的块只影响本组件;不写 scoped 的会全局生效,类名尽量用不会冲突的(如加组件名前缀)。 普通 里 export default 的选项(如 name)和 的变量/函数会合并:模板用 setup 暴露的,组件配置用 export default 的。 把本文档里的 UserCard.vue 和 App.vue 在项目里敲一遍、改一改,会掌握得更牢。更多组件通信、插槽等见《Vue3组件.md》《Vue3插槽.md》。祝你学习顺利。<template>
<div class="page">
<p>内容</p>
</div>
</template>2.2 里面能写什么?
2.3 用 src 引用外部模板(少见)
<template src="./template.html"></template>
三、 与
3.1 两种写法
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<script>
export default {
data() {
return { count: 0 }
},
methods: {
add() {
this.count++
}
}
}
</script>3.2 同时有 和
<script>
export default {
name: 'MyComponent',
inheritAttrs: false
}
</script>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
3.3 lang=”ts”:用 TypeScript
<script setup lang="ts">
import { ref } from 'vue'
const count = ref<number>(0)
</script>
四、:样式
4.1 普通 :全局
一般只在刻意写全局样式(如重置、主题变量)时用。<style>
.global-title {
font-size: 24px;
}
</style>4.2 scoped:只影响当前组件
<template>
<div class="box">
<p class="text">只有本组件的 p 会变红</p>
</div>
</template>
<style scoped>
.box {
padding: 16px;
}
.text {
color: red;
}
</style>
4.3 多个 块
<style scoped>
.component-class {
color: blue;
}
</style>
<style>
body {
margin: 0;
}
</style>
4.4 深度选择器:影响子组件内部
Vue 3 推荐用 :deep():<style scoped>
.parent :deep(.child-inner) {
color: red;
}
</style>
4.5 在 style 里用响应式数据(v-bind)
<template>
<div class="box">颜色随数据变</div>
</template>
<script setup>
import { ref } from 'vue'
const themeColor = ref('blue')
</script>
<style scoped>
.box {
background-color: v-bind(themeColor);
}
</style>
4.6 预处理器:lang=”scss” / “less”
<style scoped lang="scss">
$color: blue;
.box {
padding: 16px;
.inner {
color: $color;
}
}
</style>4.7 用 src 引用外部样式文件
<style scoped src="./styles.css"></style>
五、文件命名与组件名
5.1 文件命名
5.2 组件名(name)
六、完整示例:一个带 template / script / style 的组件
<!-- UserCard.vue -->
<template>
<div class="user-card">
<img :src="user.avatar" :alt="user.name" class="avatar" />
<div class="info">
<h3>{{ user.name }}</h3>
<p>{{ user.bio }}</p>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
user: {
type: Object,
required: true
}
})
const displayName = computed(() => props.user.name || '匿名')
</script>
<style scoped>
.user-card {
display: flex;
gap: 12px;
padding: 16px;
border: 1px solid #eee;
border-radius: 8px;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
}
.info h3 {
margin: 0 0 4px 0;
font-size: 16px;
}
.info p {
margin: 0;
color: #666;
font-size: 14px;
}
</style>
七、在别的组件里使用单文件组件
7.1 引入并使用
<!-- App.vue -->
<template>
<div>
<UserCard :user="user" />
</div>
</template>
<script setup>
import UserCard from './components/UserCard.vue'
import { ref } from 'vue'
const user = ref({
name: '小明',
avatar: '/avatar.jpg',
bio: '前端开发'
})
</script>
7.2 路径与别名
八、单文件组件结构小结
块
作用
说明
结构
一个根或多个根(Vue 3),可写 HTML + Vue 语法
逻辑
选项式:export default { data, methods, … }
逻辑
组合式,顶层自动暴露,推荐
样式
不加 scoped 为全局
样式
只影响本组件,子组件内部需 :deep()
样式
使用预处理器
九、常见问题与注意点
9.1 template 里只能有一个“顶层”或多个根
9.2 scoped 只加在“当前组件”的 DOM 上
9.3 多个 style 时 scoped 和全局别搞混
9.4 script 与 script setup 同时存在时
十、学习建议