Vue.js单文件组件(SFC)

单文件组件(SFC)是Vue.js中组织代码的推荐方式,它将模板、逻辑和样式封装在一个.vue文件中。

什么是单文件组件?

Vue单文件组件(Single-File Components,简称SFC)是一种特殊的文件格式,允许我们将一个Vue组件的模板脚本样式封装在单个文件中。

每个.vue文件由三部分组成:

  • <template> - HTML模板
  • <script> - JavaScript代码
  • <style> - CSS样式

SFC基本结构

下面是一个最简单的Vue单文件组件示例:

ExampleComponent.vue
<template>
  <div class="example">
    <h1>{{ title }}</h1>
    <p>当前计数: {{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
export default {
  name: 'ExampleComponent',
  data() {
    return {
      title: 'Vue SFC示例',
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  mounted() {
    console.log('组件已挂载')
  }
}
</script>

<style scoped>
.example {
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  background-color: #f9f9f9;
}
.example h1 {
  color: #42b983;
}
button {
  padding: 8px 16px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>

使用SFC的优势

模块化

将相关代码组织在一个文件中,提高代码的可维护性和复用性。

预处理器支持

支持使用TypeScript、SCSS、Less等现代开发工具。

作用域CSS

通过scoped属性实现组件样式隔离。

开发体验

结合Vue Devtools提供更好的调试体验。

Composition API示例

在Vue 3中,可以使用Composition API编写SFC:

CounterComponent.vue
<template>
  <div class="counter">
    <h2>计数器: {{ count }}</h2>
    <div class="buttons">
      <button @click="decrement" class="btn btn-secondary">-</button>
      <button @click="reset" class="btn btn-warning">重置</button>
      <button @click="increment" class="btn btn-primary">+</button>
    </div>
    <p>计数是: {{ parity }}</p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

// 响应式状态
const count = ref(0)

// 计算属性
const parity = computed(() => {
  return count.value % 2 === 0 ? '偶数' : '奇数'
})

// 方法
function increment() {
  count.value++
}

function decrement() {
  count.value--
}

function reset() {
  count.value = 0
}
</script>

<style scoped>
.counter {
  text-align: center;
  padding: 20px;
  max-width: 400px;
  margin: 0 auto;
}
.buttons {
  display: flex;
  gap: 10px;
  justify-content: center;
  margin: 20px 0;
}
button {
  min-width: 80px;
}
p {
  font-size: 1.2em;
  color: #666;
}
</style>

使用预处理器

SFC支持多种预处理器,让开发更高效:

StyledComponent.vue
<template>
  <div class="styled-box">
    <slot/>
  </div>
</template>

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

export default defineComponent({
  name: 'StyledBox'
})
</script>

<style lang="scss" scoped>
.styled-box {
  padding: 20px;
  margin: 10px;
  border-radius: 8px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);

  // SCSS嵌套语法
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
    transition: all 0.3s ease;
  }

  // 媒体查询
  @media (max-width: 768px) {
    padding: 15px;
    font-size: 14px;
  }
}
</style>

组件间通信

父传子 - Props
<!-- 父组件 -->
<ChildComponent :message="parentMsg" />

<!-- 子组件 -->
<script>
export default {
  props: {
    message: {
      type: String,
      required: true
    }
  }
}
</script>
子传父 - Emit
<!-- 子组件 -->
<button @click="$emit('custom-event', data)">
  触发事件
</button>

<!-- 父组件 -->
<ChildComponent @custom-event="handleEvent" />

最佳实践

  1. 每个组件专注于单一功能(单一职责原则)
  2. 为组件添加name属性,便于调试
  3. 使用scoped样式防止样式污染
  4. 复杂组件拆分为更小的子组件
  5. 使用TypeScript提高代码健壮性
  6. 为props提供详细的类型定义和默认值
常见问题

是的,.vue文件需要Vue CLI、Vite等构建工具进行编译,转换为浏览器可执行的JavaScript代码。

不推荐。每个.vue文件应该只包含一个组件,遵循单一职责原则。