SoFunction
Updated on 2025-04-03

A brief talk about the component components in Vue3

Implement a component

A component is actually a vue file. A simple example () is as follows:

<script setup></script>
<template>
  <div class="header"></div>
</template>
<style scoped lang="less">
.header {
  position: absolute;
  width: 100%;
  height: 80px;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0.6) 0%,
    rgba(0, 0, 0, 0) 100%
  );
}
</style>

Register to use

Based on script setup, components can be automatically registered and only import can be used, as follows:

<script setup>
import Header from "./";
</script>
<template>
  <div class="container">
    <Header></Header>
  </div>
</template>
<style scoped lang="less">
.container {
  width: 100%;
  height: 100%;
}
</style>

data

You can pass data to components through Prop, and first define it in the component, as follows:

<script setup>
const props = defineProps({
  title: String,
  userInfo: Object
});
function doSomething(){
  if(){
    ...
  }
}
</script>
<template>
  <div class="header">{{}}</div>
</template>
<style scoped lang="less">
...
</style>

Here a title attribute is defined, which is a string; a userInfo attribute is an object, which can be passed in the component.to use these properties.

So how to pass data to these properties? Just bind the data directly through v-bind, as follows:

<script setup>
import Header from "./";
let userInfo;
let title;
</script>
<template>
  <div class="container">
    <Header :title="title" :userInfo="userInfo"></Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

Here is the abbreviation of v-bind. v-bind binding is an expression after the double quotes, so if the type is:

  • Value::count="3"
  • Boolean value::isVip="true"
  • Array::array="[1,2,3]"
  • Object::info="{name:'name',isVip:true}"I won't list the others one by one.

Props is supported as follows:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol。

Props supports type checking and also supports default values, as follows:

  props: {
    // Basic type checking (`null` and `undefined` values ​​will pass any type verification)    propA: Number,
    // Multiple possible types    propB: [String, Number],
    // Required string    propC: {
      type: String,
      required: true
    },
    // Number with default value    propD: {
      type: Number,
      default: 100
    },
    // Object with default value    propE: {
      type: Object,
      // The default value of an object or array must be returned from a factory function      default() {
        return { message: 'hello' }
      }
    },
    // Function with default values    propG: {
      type: Function,
      // Unlike the default values ​​of an object or array, this is not a factory function - this is a function used as the default values      default() {
        return 'Default function'
      }
    }
  }

Props are a one-way data stream, which prevents child components from accidentally changing the state of the parent component. Whenever the parent component changes, all the child components' Props will be refreshed to the latest value. Therefore, the properties of Props cannot be changed in the child component, otherwise it will be warned in the console.

Non-Prop Attribute

for exampleclassstyleandidetc attribute, by default, it is added to the root node of the component, such as:

<script setup>
...
</script>
<template>
  <div class="header" >
    <input ...>
  </div>
</template>
<style scoped lang="less">
...
</style>

When we use this component, add an attribute to it, such as:

<Header ></Header>

The actual rendering result is:

<div class="header" >
  <input ...>
</div>

Disable Attribute inheritance

If you do not want to add to the root node, you can set itinheritAttrs: false

<script setup>Can't declare inheritAttrs, so we need to add normal<script>, use it at the same timev-bind="$attrs"To bind nodes that inherit Attribute, such as

<script>
export default {
  inheritAttrs: false
};
</script>
<script setup>
...
</script>
<template>
  <div class="header" >
    <input ... v-bind="$attrs">
  </div>
</template>
<style scoped lang="less">
...
</style>

In this way, the Attribute added to the component will be directly added to the input element. The actual rendering result is:

<div class="header">
  <input ...  >
</div>

Multiple nodes

If the component has multiple nodes, it must be explicitly setv-bind="$attrs", otherwise warning.

event

The data is delivered clearly, so how do you respond to some custom events of the component? Define events through emits, as follows:

<script setup>
const emit = defineEmits(["onSelected"]);
function selectItem(item){
  emit("onSelected", item);
}
</script>
<template>
  <div class="header" >
    ...
  </div>
</template>
<style scoped lang="less">
...
</style>

Here is a defined onSelected event, which will be executed when the selectItem function is triggered in the component.

Note: If there are no parameters, thenemit("onSelected")If there are multiple parameters,emit("onSelected", param1, param2, ...)

In the parent component, events are bound through v-on, as follows:

<script setup>
import Header from "./";
function headerSelected(item){
}
</script>
<template>
  <div class="container">
    <Header @onSelected="headerSelected"></Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

This is also the abbreviation of v-on, so the event is bound. Events can also be verified, so I won’t go into details here.

v-model

v-model is a two-way data binding, and by default, v-model on the component uses modelValue as prop and update:modelValue as events. For example, there is a title attribute:

<my-component v-model:title="bookTitle"></my-component>

Then in the child component you can do this:

<script setup>
const props = defineProps({
  title: String
});
const emit = defineEmits(["update:title"]);
const setTitle = (newTitle) => {
  emit("update:title", newTitle);
}
</script>
<template>
  <div class="header" >
    ...
  </div>
</template>
<style scoped lang="less">
...
</style>

This way, the subcomponent can passupdate:titleTo synchronize title data.

Slot

What if some areas in a child component are uncertain and need the parent component to implement it? This requires slot slot. The slot is very simple to use, as follows:

<script setup>
...
</script>
<template>
  <div class="header" >
    <slot>default</slot>
  </div>
</template>
<style scoped lang="less">
...
</style>

Here a slot is defined and a default content is specified, in the parent component:

<script setup>
import Header from "./";
</script>
<template>
  <div class="container">
    <Header>newtitle</Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

In this way the slot is replaced by the string "newtitle" if there is nothing here<Header></Header>Then the default content is displayed, that is, "default".

The slot is not only a string, but can be html or other components, such as:

<Header>
 <button>submit</botton>
</Header>

Of course, the default content can also be html.

Named slots

There can be multiple slots in a component, such as the left and right columns, so named slots are needed to distinguish them, as follows:

<script setup>
...
</script>
<template>
  <div class="header" >
    <div name="leftbar" >
      <slot>left</slot>
    </div>
    <div name="rightbar" >
      <slot>right</slot>
    </div>
  </div>
</template>
<style scoped lang="less">
...
</style>

When using it, you need to use the v-slot command, and it must be<template>element, because v-slot can only be added in<template>Above, as follows:

<script setup>
import Header from "./";
</script>
<template>
  <div class="container">
    <Header>
      <template v-slot:leftbar >
        <button>left</button>
      </template>
      <template v-slot:rightbar >
        <button>right</button>
      </template>
    </Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

The slot name can also be dynamic<template v-slot:[dynamicSlotName] >. v-slot can also be abbreviated as "#", such as<template #leftbar >

Scope

The following code:

<div v-for="item in list" >
  <slot></slot>
</div>

At this time, when we use components, there is no item in the slot, as follows

<my-component >
  <img :src=""></img>
</my-component>

Codes like the above will report an error because the item can only be accessed in the component, while the slots are provided on the parent component and have different scopes.

Of course we can add an attribute binding to the slot, i.e.Slot prop,as follows:

<div v-for="item in list" >
  <slot :item="item"></slot>
</div>

Slots can add multiple attributes.

Then use the value in the parentv-slotTo define the name of the slot prop we provide, such as:

<my-component >
  <template v-slot:default="slotProps">
    <img :src=""></img>
  </template>   
</my-component>

This way there will be no errors. If there is only one slot,v-slotYou can directly add components, such as:

<my-component v-slot="slotProps">
  <img :src=""></img> 
</my-component>

The template element is not needed; however, if there are multiple slots (named) it must be added to the template element.

Provide / Inject

The above knows that the parent component uses Props to pass data to the child component, but if the component level is very deep, it needs to pass data to an underlying child component. If you use Props, you need to pass it layer by layer. You can use Provide / Inject at this time. For example: Passed in the parent componentprovide(key, value)To set the data

<script setup>
const userid = "123";
provide("userid", userid);
</script>
...

Then pass in the subcomponentinject(key, defaultValue)To get data

<script setup>
const userid = inject("userid", defaultId);
</script>
...

Of course, the above is one-time data. If a response is required, use ref or reactive in the parent component, such as:

<script setup>
const userid = ref("123");
provide("userid", userid);
</script>
...

Get DOM object

In the component, we can set the id to the element and get its DOM object through the id. But in the case where there are multiple components on the page, it will be problematic to get the DOM object in this way because the id is not unique.

We can also userefattribute Specifies a reference ID for a child component or HTML element. like:

<script setup>
import Header from "./";
const headerRef = ref();
</script>
<template>
  <div class="container">
    <Header ref="headerRef">
      ...
    </Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

The ref attribute of the header above is "headerRef", so the variable name is also consistent, that is,const headerRef = ref();,soIt is its DOM object, which can be executedgetElementsByTagNameetc.

This means that there are multiple components on the page, but because of the scope, each headerRef does not interfere with each other.

List

What if it is in a list? for example:

<template>
  <div class="container">
    <item ref="itemRef" v-for="item in list">
      ...
    </item>
  </div>
</template>

This wayconst itemRef = ref();Which one do you get? Actually, that's itIt becomes a list.[0]It's the first oneitemThe object of the component.

Element position restricted

Some HTML elements are<ul><ol><table>and<select>, there are strict restrictions on its internal limits. Some elements such as<li><tr>and<option>, can only appear inside certain specific elements.

This causes us to have some problems using these constrained elements. For example:

<table>
  <my-component></my-component>
</table>

Custom components<my-component>It will be promoted to the outside as invalid content, resulting in rendering errors. You need to use it at this timeisattribute, such as:

<table>
  <tr is="vue:my-component"></tr>
</table>

Notice:isThe value of   must bevue:Only at the beginning can it be interpreted as a Vue component. This is to avoid confusion with native custom elements.

Calling child component method

The above event chapter talks about the event of the parent component responding to the child component, that is, the method of the child component calling the parent component. So how does the parent component call the child component's method?

Expose

First of all, the method of subcomponents needs to be exposed, as follows:

<script setup>
defineExpose({
  onEvent
});
function onEvent(event) {
  (`header: ${event}`);
}
</script>
<template>
  <div class="header" >
    ...
  </div>
</template>
<style scoped lang="less">
...
</style>

Then add a ref attribute to the child component in the parent component, and then use the ref() function to obtain the object execution method, as follows:

<script setup>
import Header from "./";
const headerRef = ref();
function headerEvent(event){
  (event);
}
</script>
<template>
  <div class="container">
    <Header ref="headerRef">
      ...
    </Header>
  </div>
</template>
<style scoped lang="less">
...
</style>

This way it can passExposed methods are executed.

If there are multiple subcomponents and all have the ref attribute set, you can define multiple variables, as follows:

const headerRef = ref();
const footerRef = ref();

EventBus

The simpler method is to use EventBus to complete communication between components, and it is also very simple to use. Please refer to the official documentation./package/events

We can simply encapsulate it for use, as follows:

import EventEmitter from "events";
const eventEmitter = new EventEmitter();
export const WsEvent = {
  emit: data => {
    ("wsevent", data);
  },
  on: callback => {
    ("wsevent", callback);
  }
};

The above is a detailed description of the component component in Vue3. For more information about Vue3 component, please follow my other related articles!