第一部分:Vue 3 基础入门
第6集:单文件组件(SFC)揭秘
Vue 3的单文件组件(Single-File Component,简称SFC)是Vue开发的核心特性之一。它将组件的模板、逻辑和样式封装在一个文件中,提供了一种高效、组织良好的组件开发方式。在本集中,我们将深入了解SFC的结构、特性和最佳实践。
6.1 单文件组件的基本结构
一个Vue单文件组件通常包含三个部分:
<template>:组件的模板,定义组件的HTML结构<script>或<script setup>:组件的逻辑,定义组件的数据、方法、生命周期等<style>:组件的样式,定义组件的外观
6.2 <template> 部分
作用:定义组件的HTML结构,包含组件的视图模板。
特点:
- 每个SFC只能有一个
<template>标签 - 支持Vue的模板语法,如插值、指令、条件渲染、列表渲染等
- 模板中的根元素可以是多个(Vue 3支持Fragment)
- 支持使用
v-bind或:绑定属性 - 支持使用
v-on或@绑定事件
示例:
<template>
<div class="greeting">
<h1>{{ message }}</h1>
<button @click="increment">点击计数</button>
<p>计数: {{ count }}</p>
</div>
<div class="info">
<p>这是另一个根元素</p>
</div>
</template>6.3 <script> 部分
作用:定义组件的逻辑,包括数据、方法、生命周期钩子等。
Vue 3支持两种脚本语法:
6.3.1 传统选项式API
<script>
export default {
name: 'Greeting',
data() {
return {
message: 'Hello Vue 3!',
count: 0
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
console.log('组件已挂载')
}
}
</script>6.3.2 组合式API (<script setup>)
<script setup>是Vue 3.2+推荐的语法,提供了更简洁、更高效的开发体验。
特点:
- 自动注册组件,无需
export default - 直接导入和使用组件,无需注册
- 自动解析顶层变量和函数,可在模板中直接使用
- 更好的TypeScript支持
- 更小的打包体积
示例:
<script setup>
import { ref, onMounted } from 'vue'
// 定义响应式数据
const message = ref('Hello Vue 3!')
const count = ref(0)
// 定义方法
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log('组件已挂载')
})
</script>6.4 <style> 部分
作用:定义组件的样式,包括CSS、SCSS、Less等。
特点:
- 支持多种CSS预处理器,如SCSS、Less、Stylus等
- 支持作用域样式(使用
scoped属性) - 支持CSS Modules(使用
module属性) - 可以有多个
<style>标签,用于不同的样式需求
6.4.1 全局样式
<style>
/* 全局样式,会影响所有组件 */
body {
font-family: Arial, sans-serif;
}
</style>6.4.2 作用域样式 (scoped)
使用scoped属性可以使样式只作用于当前组件,避免样式冲突。
<style scoped>
/* 只作用于当前组件的样式 */
.greeting {
background-color: #f0f0f0;
padding: 20px;
border-radius: 8px;
}
button {
background-color: #42b883;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
</style>6.4.3 CSS Modules (module)
使用module属性可以将CSS作为模块导入,避免样式名冲突。
<style module>
/* CSS Modules */
.container {
background-color: #f0f0f0;
padding: 20px;
}
.title {
color: #35495e;
font-size: 24px;
}
</style>
<template>
<div :class="$style.container">
<h1 :class="$style.title">{{ message }}</h1>
</div>
</template>6.4.4 使用CSS预处理器
需要先安装相应的预处理器依赖,然后在<style>标签中指定语言。
安装SCSS依赖:
npm install -D sass使用SCSS:
<style scoped lang="scss">
$primary-color: #42b883;
$secondary-color: #35495e;
.greeting {
background-color: lighten($primary-color, 20%);
padding: 20px;
border-radius: 8px;
h1 {
color: $secondary-color;
margin-bottom: 10px;
}
button {
background-color: $primary-color;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: darken($primary-color, 10%);
}
}
}
</style>6.5 单文件组件的高级特性
6.5.1 组件导入和使用
在<script setup>中,可以直接导入和使用组件,无需注册。
<script setup>
import HelloWorld from './HelloWorld.vue'
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<HelloWorld msg="Hello from Parent" />
<p>Count: {{ count }}</p>
</template>6.5.2 Props定义
使用defineProps函数定义组件的props。
<script setup>
const props = defineProps({
msg: {
type: String,
default: 'Hello'
},
count: {
type: Number,
required: true
}
})
</script>
<template>
<div>
<p>{{ msg }}</p>
<p>Count: {{ count }}</p>
</div>
</template>6.5.3 自定义事件
使用defineEmits函数定义组件可以触发的事件。
<script setup>
const emit = defineEmits(['increment', 'decrement'])
function handleIncrement() {
emit('increment')
}
function handleDecrement() {
emit('decrement', 2) // 可以传递参数
}
</script>
<template>
<div>
<button @click="handleIncrement">+</button>
<button @click="handleDecrement">-</button>
</div>
</template>6.5.4 计算属性
使用computed函数定义计算属性。
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const isEven = computed(() => count.value % 2 === 0)
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<p>Is Even: {{ isEven ? 'Yes' : 'No' }}</p>
<button @click="count++">Increment</button>
</div>
</template>6.5.5 侦听器
使用watch或watchEffect函数定义侦听器。
<script setup>
import { ref, watch, watchEffect } from 'vue'
const count = ref(0)
const message = ref('')
// 监听单个值
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`)
})
// 监听多个值
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
console.log('Count or message changed')
})
// 自动跟踪依赖
watchEffect(() => {
console.log(`Count: ${count.value}, Message: ${message.value}`)
})
</script>6.6 单文件组件的最佳实践
组件命名:
- 组件名使用PascalCase,如HelloWorld.vue
- 文件名与组件名保持一致
- 避免使用Vue保留字作为组件名
组件职责:
- 每个组件只负责一个功能
- 组件大小适中,避免过大的组件
- 复杂组件可以拆分为多个子组件
样式管理:
- 优先使用
scoped样式,避免样式冲突 - 复杂样式可以使用CSS预处理器
- 全局样式放在专门的文件中
- 优先使用
脚本语法:
- 优先使用
<script setup>语法 - 合理使用组合式API,按逻辑组织代码
- 避免在模板中写复杂的表达式
- 优先使用
性能优化:
- 使用
v-memo优化频繁渲染的列表 - 使用
v-once优化静态内容 - 合理使用
computed缓存计算结果 - 避免在模板中使用复杂的计算
- 使用
代码组织:
- 导入语句按字母顺序排列
- 先导入Vue核心API,再导入组件,最后导入工具函数
- 响应式数据、计算属性、方法、生命周期钩子按逻辑顺序组织
6.7 单文件组件的编译过程
Vue单文件组件需要经过编译才能在浏览器中运行。编译过程包括:
- 模板编译:将模板转换为渲染函数
- 脚本编译:处理脚本,包括TypeScript转译、代码优化等
- 样式编译:处理样式,包括预处理器转译、CSS Modules处理等
- 合并输出:将编译后的模板、脚本和样式合并为最终的JavaScript模块
在开发环境中,Vite会实时编译SFC;在生产环境中,Vite会将SFC编译为优化后的JavaScript文件。
本集小结
在本集中,我们深入了解了Vue 3单文件组件的结构、特性和最佳实践:
- 基本结构:
<template>、<script>/<script setup>和<style> - 模板部分:支持Vue模板语法,Vue 3支持多个根元素
- 脚本部分:支持传统选项式API和组合式API
- 样式部分:支持全局样式、作用域样式和CSS Modules
- 高级特性:组件导入、Props定义、自定义事件、计算属性、侦听器等
- 最佳实践:组件命名、职责划分、样式管理、性能优化等
单文件组件是Vue开发的核心,掌握好SFC的使用可以提高开发效率和代码质量。在下一集中,我们将学习Vue 3的模板语法基础,包括插值和指令。