在 Vue 3 中,组件是构建应用的核心部分,允许你将 UI 分解成独立、可复用的代码片段。Vue 组件的结构通常包括模板、脚本和样式。你可以创建全局组件和局部组件,并通过不同的方式在模板中使用它们。

1. 组件基本结构

Vue 3 组件的基本结构包含了模板、脚本和样式三个部分。你可以把它们写在一个 .vue 文件中,也可以分开来管理。

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
export default {
  name: 'MyComponent',
  data() {
    return {
      message: 'Hello from Vue 3 component!'
    };
  }
};
</script>

<style scoped>
h1 {
  color: blue;
}
</style>

2. 定义组件

组件可以通过 Vue.component() 注册为全局组件,或者在某个父组件中作为局部组件进行注册。

2.1. 全局组件

你可以在应用的入口文件中(通常是 main.jsmain.ts)注册全局组件。这样,组件在任何地方都可以使用。

// main.js
import { createApp } from 'vue';
import App from './App.vue';
import MyComponent from './components/MyComponent.vue';

const app = createApp(App);
app.component('MyComponent', MyComponent);
app.mount('#app');

全局注册的组件可以在任何模板中直接使用:

<template>
  <div>
    <MyComponent />
  </div>
</template>

2.2. 局部组件

你也可以在父组件中局部注册子组件。局部组件只会在父组件的模板中可用。

<script>
import MyComponent from './components/MyComponent.vue';

export default {
  components: {
    MyComponent
  }
};
</script>

<template>
  <div>
    <MyComponent />
  </div>
</template>

3. 组件传递数据(Props)

父组件可以通过 props 向子组件传递数据。props 是子组件的输入,父组件可以通过它传递数据,子组件可以通过 this.$props 访问这些数据。

<!-- Parent.vue -->
<template>
  <div>
    <ChildComponent :message="parentMessage" />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: 'Hello from parent!'
    };
  }
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: {
    message: String
  }
};
</script>

4. 组件事件(Emit)

子组件可以通过 $emit 向父组件发送事件,父组件可以通过 v-on@ 监听这些事件。这样,父组件可以与子组件进行通信。

<!-- Parent.vue -->
<template>
  <div>
    <ChildComponent @customEvent="handleCustomEvent" />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    handleCustomEvent(data) {
      console.log('Received data:', data);
    }
  }
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <button @click="sendEvent">Send Event</button>
  </div>
</template>

<script>
export default {
  methods: {
    sendEvent() {
      this.$emit('customEvent', 'Hello from Child!');
    }
  }
};
</script>

5. 组件的插槽(Slots)

插槽是 Vue 中的一种内容分发机制。它允许你将内容插入到子组件的预定位置,从而使得子组件更加灵活。

5.1. 默认插槽

<!-- Parent.vue -->
<template>
  <ChildComponent>
    <p>This is content from parent</p>
  </ChildComponent>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  }
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <slot></slot>  <!-- 插槽插入的内容会显示在这里 -->
  </div>
</template>

5.2. 命名插槽

命名插槽允许你在组件中定义多个插槽,并通过名字来区分。

<!-- Parent.vue -->
<template>
  <ChildComponent>
    <template #header>
      <h1>This is a header</h1>
    </template>
    <template #footer>
      <p>This is a footer</p>
    </template>
  </ChildComponent>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  }
};
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <header><slot name="header"></slot></header>
    <footer><slot name="footer"></slot></footer>
  </div>
</template>

6. 计算属性和侦听器(Computed and Watchers)

  • 计算属性(Computed):计算属性是基于它们的依赖进行缓存的,因此只有在相关依赖发生变化时,才会重新计算。
<template>
  <div>
    <p>Original Price: {{ price }}</p>
    <p>Discounted Price: {{ discountedPrice }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      price: 100
    };
  },
  computed: {
    discountedPrice() {
      return this.price * 0.9;  // 10% discount
    }
  }
};
</script>

  • 侦听器(Watchers):侦听器用于观察数据的变化,并执行一些异步或开销较大的操作。
<template>
  <div>
    <input v-model="price" />
    <p>Price changed: {{ price }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      price: 100
    };
  },
  watch: {
    price(newValue, oldValue) {
      console.log(`Price changed from ${oldValue} to ${newValue}`);
    }
  }
};
</script>

7. 动态组件

动态组件允许你在应用中根据条件渲染不同的组件。

<template>
  <component :is="currentComponent"></component>
  <button @click="changeComponent">Change Component</button>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentComponent: ComponentA
    };
  },
  methods: {
    changeComponent() {
      this.currentComponent = this.currentComponent === ComponentA ? ComponentB : ComponentA;
    }
  }
};
</script>

8. 组合式 API(Composition API)

Vue 3 引入了组合式 API,允许你以函数的方式来组织代码。

<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

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

export default {
  setup() {
    const message = ref('Hello from Composition API!');
    return {
      message
    };
  }
};
</script>

总结

Vue 3 组件是构建应用的核心,支持非常灵活的数据传递(props 和 emit)、事件处理、插槽、计算属性、侦听器等功能。通过组合式 API,你可以更加灵活和清晰地组织和复用逻辑。组件的设计使得 Vue 应用能够易于扩展、维护和测试。