【Vue3】组件

2022/12/05 16:00:062022/12/02 15:27:43

注册组件

全局注册

在应用上注册的组件是全局组件,可在应用下的所有组件上使用(多个全局组件可互相引用),无需再次注册。

import { createApp } from "vue";
import MyComponent from "./App.vue";
import MyComponent2 from "./App2.vue";

const app = createApp({});

// 注册方法可链式调用
app
  .component("MyComponent", MyComponent)
  .component("MyComponent2", MyComponent2);

全局注册组件的缺点:

  • 不会被 tree-shaking,打包体积变大。
  • 会导致依赖关系不明确,增加维护难度。

局部注册

局部注册的组件无法在后代组件中使用。

<script setup> 中导入的组件可直接使用,无需注册:

<script setup>
import ComponentA from './ComponentA.vue'
</script>

<template>
  <ComponentA />
</template>

选项式 API 需要显示注册组件。

根节点

Vue3 中组件可以拥有多个根节点:

  • myComp.vue
<template>
  t1:{{ title }}
  <input type="text" />
  <button @click="onBtnClick()">btn-t1</button>
</template>

单个根节点时,在组件上绑定的 class 会直接绑定到根元素上,多个根节点时,需要用组件的 $attrs 属性来指定哪个元素来接收 class

组件配置(<script setup>

props

使用 defineProps() 方法声明 props,声明的 props 可以直接在模板中使用,该方法返回一个 proxy 对象,其中包含了可以传递给组件的所有 props。

props 对象形式的完整配置open in new window和 Vue2 类似,可配置默认值和校验函数。

import { defineProps } from "vue";

// 简单字符串形式声明
const props = defineProps(["title"]);

// 对象形式声明
defineProps({
  title: String,
  likes: Number,
});

事件

在组件中使用 defineEmits() 方法声明可在组件上绑定的事件,该方法返回一个函数,作用与 $emit() 函数一致,可调用该方法在组件中抛出事件。

import { defineEmits } from "vue";
const emit = defineEmits(["enlarge-text"]);

function onBtnClick() {
  emit("enlarge-text", 91);
}

选项式 API 可在 emits 选项中声明事件,同时可在 setup() 方法的第二个参数,即 setup 上下文对象上访问到 emit 函数:

export default {
  emits: ["enlarge-text"],
  setup(props, ctx) {
    ctx.emit("enlarge-text");
  },
};

所有未在子组件内的声明的事件都将被认为是原生事件。

slot 插槽

useSlots() 方法获取插槽的引用对象。

import { useSlots } from "vue";

const slots = useSlots();

透传 attribute

在组件上绑定的属性、事件如果没有在组件内定义,则会被视为透传的 attribute,当组件只有一个根元素时这些透传 attribute 会被添加到根元素上。

透传进来的属性可以在组件模板中用 $attrs 访问,同时 $attrs 也包含了除组件所声明的 props 和 emits 之外的所有其他 attribute。

可以在 <script setup> 中使用 useAttrs() 方法来访问 $attrs

<script setup>
import { useAttrs } from "vue";

const attrs = useAttrs();
</script>

使用选项式 Api 时可以在 setup() 方法中访问,或者用 this.$attrs 访问:

export default {
  setup(props, ctx) {
    // 透传 attribute 被暴露为 ctx.attrs
    console.log(ctx.attrs);
  },
  mounted() {
    console.log(this.$attrs);
  },
};

单根节点下透传 attribute 会自动继承

假设 <mycomp/> 是一个单根节点组件且未声明 click 事件,下例中的 click 事件会直接绑定在 <mycomp/> 组件的根元素上。

<mycomp @click="handle" />

要禁止组件自动继承透传 attribute 需要在组件选项中设置 inheritAttrs: false

<script>
// 使用普通的 <script> 来声明选项
export default {
  inheritAttrs: false,
};
</script>

defineExpose()

使用 <script setup> 的组件是默认关闭的,需要通过 defineExpose() 方法定义要暴露的属性,与 setup() 中的 expose() 方法一致。

使用组件

v-bind

直接用 v-bind 给组件传递一个对象可以便捷的将对象的每个属性传递给 props:

<template>
  <BlogPost v-bind="post" />
  <!-- 等价于 -->
  <BlogPost :id="post.id" :title="post.title" />
</template>
<script setup>
const post = {
  id: 1,
  title: "My Journey with Vue",
};
</script>

Boolean 类型的 props 可在使用组件时只绑定属性名,会在组件中自动转换为 true

<template>
  <!-- 等同于传入 :disabled="true" -->
  <MyComponent disabled />
  <!-- 等同于传入 :disabled="false" -->
  <MyComponent />
</template>
<script setup>
defineProps({
  disabled: Boolean,
});
</script>

v-model

异步组件

Vue3 中用 defineAsyncComponent() 方法声明异步组件。

import { defineAsyncComponent } from "vue";

// 注册异步组件
app.component(
  "MyComponent",
  defineAsyncComponent(() => import("./components/MyComponent.vue"))
);

defineAsyncComponent() 方法返回一个 resolve 组件对象的 Promise。

import { defineAsyncComponent } from "vue";

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */);
  });
});
// ... 像使用其他一般组件一样使用 `AsyncComp`

如果要处理错误状态、加载失败后展示的组件等逻辑可以使用高级选项open in new window