VUE3 components

Components Basics {#components-basics}

Components allow us to divide the UI into independent, reusable parts and think about each part separately. In practical applications, components are often organized into a nested tree structure:

component tree

Similar to how we nest HTML elements, Vue implements its own component model that allows us to encapsulate custom content and logic within each component. Vue also works well with native Web Components. If you want to know the relationship between Vue components and native Web Components, you can read this chapter .

define a component {#defining-a-component}

When using build steps, we generally define Vue components in a separate .vuefile, which is called a single-file component (SFC for short):

<script>
export default {
  data() {
    return {
      count: 0
    }
  }
}
</script>

<template>
  <button @click="count++">You clicked me {
   
   { count }} times.</button>
</template>
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button @click="count++">You clicked me {
   
   { count }} times.</button>
</template>

When not using a build step, a Vue component is defined as a JavaScript object containing Vue-specific options:

export default {
    
    
  data() {
    
    
    return {
    
    
      count: 0
    }
  },
  template: `
    <button @click="count++">
      You clicked me {
     
     { count }} times.
    </button>`
}
import {
    
     ref } from 'vue'

export default {
    
    
  setup() {
    
    
    const count = ref(0)
    return {
    
     count }
  },
  template: `
    <button @click="count++">
      You clicked me {
     
     { count }} times.
    </button>`
  // 也可以针对一个 DOM 内联模板:
  // template: '#my-template-element'
}

The template here is an inline JavaScript string that Vue will compile at runtime. You can also use an ID selector to point to an element (usually a native <template>element), and Vue will use its content as a template source.

In the example above, a component is defined and .jsexported by default in a file, but you can also export multiple components in a file through named exports.

Using a component {#using-a-component}

:::tip
We will be using SFC syntax in the following tutorials, the concepts related to components are the same whether you use build steps or not. The usage of components in two scenarios is shown in the Examples section.
:::

To use a child component, we need to import it in the parent component. Suppose we put the counter component in a ButtonCounter.vuefile called , this component will be exposed to the outside world in the form of default export.

<script>
import ButtonCounter from './ButtonCounter.vue'

export default {
  components: {
    ButtonCounter
  }
}
</script>

<template>
  <h1>Here is a child component!</h1>
  <ButtonCounter />
</template>

To expose an imported component to templates, we need to registercomponents it on options . This component will use the name it was registered with as the tag name in the template.

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

<template>
  <h1>Here is a child component!</h1>
  <ButtonCounter />
</template>

Passed <script setup>, the imported components are directly available in the template.

Of course, you can also register a component globally so that it can be used by any component in the current application without additional imports. Regarding the advantages and disadvantages of global registration and local registration of components, we will discuss them in the chapter of component registration .

Components can be reused any number of times:

<h1>Here is a child component!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />

Try it out in the playground

Try it out in the playground

You'll notice that every time these buttons are clicked, each component maintains its own state, which is different count. This is because every time you use a component, a new instance is created .

In single-file components, it is recommended to use PascalCasethe tag name for child components to distinguish it from native HTML elements. Although native HTML tag names are case-insensitive, Vue single-file components are case-sensitive in compilation. We can also use />to close a tab.

If you write the template directly in the DOM (such as the content of native <template>elements), the compilation of the template needs to follow the parsing behavior of HTML in the browser. In this case, you should need to use kebab-casethe form and explicitly close the tags of these components.

<!-- 如果是在 DOM 中书写该模板 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>

See DOM Template Parsing Considerations for more details.

passing props {#passing-props}

If we're building a blog, we'll probably need a component that represents a blog post. We want all blog posts to share the same visual layout, but have different content. To achieve such an effect, it is natural to pass data to the component, such as the title and content of each article, which will use props.

Props are a special kind of attributes that you can declare and register on components. To pass a title to the blog post component, we must declare it on the component's props list. propsThe options definePropsmacro is used here :

<!-- BlogPost.vue -->
<script>
export default {
  props: ['title']
}
</script>

<template>
  <h4>{
   
   { title }}</h4>
</template>

When a value is passed to prop, it becomes a property on that component instance. The value of this property can be accessed in the context of templates and components like other component properties this.

<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
</script>

<template>
  <h4>{
   
   { title }}</h4>
</template>

definePropsis a <script setup>compilation macro available only in and does not need to be explicitly imported. Declared props are automatically exposed to templates. definePropswill return an object containing all props that can be passed to the component:

const props = defineProps(['title'])
console.log(props.title)

For TypeScript users, please refer to: Annotating types for component props

If you don't use it <script setup>, props must be propsdeclared as an option, and the props object will setup()be passed as the first parameter of the function:

export default {
    
    
  props: ['title'],
  setup(props) {
    
    
    console.log(props.title)
  }
}

A component can have any number of props, and by default, all props accept any type of value.

When a prop is registered, data can be passed to it as a custom attribute like this:

<BlogPost title="My journey with Vue" />
<BlogPost title="Blogging with Vue" />
<BlogPost title="Why Vue is so fun" />

In a practical application, we may have an array of blog posts in the parent component as follows:

export default {
    
    
  // ...
  data() {
    
    
    return {
    
    
      posts: [
        {
    
     id: 1, title: 'My journey with Vue' },
        {
    
     id: 2, title: 'Blogging with Vue' },
        {
    
     id: 3, title: 'Why Vue is so fun' }
      ]
    }
  }
}
const posts = ref([
  {
    
     id: 1, title: 'My journey with Vue' },
  {
    
     id: 2, title: 'Blogging with Vue' },
  {
    
     id: 3, title: 'Why Vue is so fun' }
])

In this case, we can use v-forto render them:

<BlogPost
  v-for="post in posts"
  :key="post.id"
  :title="post.title"
 />

Try it out in the playground

Try it out in the playground

Notice how we use v-bindto pass dynamic prop values. This is especially useful when the exact content to be rendered is not known in advance.

That's all you need to know about props for now. If you want to know more details after reading this chapter, we recommend you to read the full guide on props in depth .

Listening to events {#listening-to-events}

Let's move on to our <BlogPost>component. We will find that sometimes it needs to interact with the parent component. For example, to achieve accessibility requirements here, the text of the blog post can be enlarged, while the rest of the page still uses the default font size.

In the parent component, we can add a postFontSize data attribute ref to achieve this effect:

data() {
  return {
    posts: [
      /* ... */
    ],
    postFontSize: 1
  }
}
const posts = ref([
  /* ... */
])

const postFontSize = ref(1)

Use this in the template to control the font size of all blog posts:

<div :style="{ fontSize: postFontSize + 'em' }">
  <BlogPost
    v-for="post in posts"
    :key="post.id"
    :title="post.title"
   />
</div>

Then, <BlogPost>add a button to the component:

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{
   
   { title }}</h4>
    <button>Enlarge text</button>
  </div>
</template>

The button doesn't do anything yet, we want to click the button to tell the parent component that it should enlarge the text of all blog posts. To solve this problem, component instances provide a custom event system. The parent component can selectively listen to the events thrown by the child component through v-onor , just like listening to native DOM events:@

<BlogPost
  ...
  @enlarge-text="postFontSize += 0.1"
 />

Subcomponents can throw an event by calling the built-in $emitmethod , passing in the event name:

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{
   
   { title }}</h4>
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

Because of @enlarge-text="postFontSize += 0.1"the monitoring of , the parent component will receive this event and update postFontSizethe value of .

Try it out in the playground

Try it out in the playground

We can declare the events that need to be thrown through emitsthe options defineEmitsmacro :

<!-- BlogPost.vue -->
<script>
export default {
  props: ['title'],
  emits: ['enlarge-text']
}
</script>
<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
defineEmits(['enlarge-text'])
</script>

This declares all the events that a component may fire, and also validates the parameters of the events . At the same time, this also allows Vue to avoid implicitly applying them as native event listeners to the root element of child components.

Similar to defineProps, defineEmitsonly available in and does not need to be imported, it returns a function <script setup>equivalent to $emitthe method . emitIt can be used to <script setup>throw events in the component's , since there is no direct access here $emit:

<script setup>
const emit = defineEmits(['enlarge-text'])

emit('enlarge-text')
</script>

For TypeScript users, please refer to: Annotating types for component emits

If you are not using it <script setup>, you can emitsdefine the events that the component will throw through options. You can setup()access emitthe function from the second parameter of the function, which is the setup context object:

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

That's all you need to know about component custom events so far. If you want to know more details after reading this chapter, please read the component events chapter in depth.

Distribute content with slots {#content-distribution-with-slots}

In some cases we would like to pass content to components like HTML elements:

<AlertBox>
  Something bad happened.
</AlertBox>

We expect it to render like this:

:::danger This is an Error for Demo Purposes
Something bad happened.
:::

This can be achieved with Vue's custom <slot>element:

<template>
  <div class="alert-box">
    <strong>This is an Error for Demo Purposes</strong>
    <slot />
  </div>
</template>

<style scoped>
.alert-box {
  /* ... */
}
</style>

As shown above, we use <slot>as a placeholder, the content passed in by the parent component will be rendered here.

Try it out in the playground

Try it out in the playground

That's all you need to know about slots for now. If you want to know more details after reading this chapter, please read the component slots chapter in depth.

Dynamic Components {#dynamic-components}

Some scenarios will require switching back and forth between two components, such as the Tab interface:

Check out the examples in the Playground

Check out the examples in the Playground

The above example is implemented with Vue's <component>element and special attribute:is

<!-- currentTab 改变时组件也改变 -->
<component :is="currentTab"></component>
<!-- currentTab 改变时组件也改变 -->
<component :is="tabs[currentTab]"></component>

In the example above, :isthe value passed to can be any of the following:

  • The name of the component being registered
  • imported component object

You can also use isattributes to create regular HTML elements.

When using <component :is="...">to switch between multiple components, the switched component will be unloaded. We can use <KeepAlive>the component to force the switched component to remain "alive".

DOM Template Parsing Caveats {#dom-template-parsing-caveats}

If you want to write the Vue template directly in the DOM, Vue must get the template string from the DOM. Due to limitations in the browser's native HTML parsing behavior, there are some caveats.

:::tip
Note that the following discussion only applies to writing templates directly in the DOM. You don't need to worry about these restrictions if you use string templates from the following sources:

  • single file component
  • Inline template strings (eg template: '...')
  • <script type="text/x-template">
    :::

case sensitivity {#case-insensitivity}

HTML tag and attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. This means that when you use a template in the DOM, whether it is a component name in PascalCase form, a prop name in camelCase form or an event name in v-on, you need to convert it to the corresponding equivalent kebab-case (dash hyphen ) of the form:

// JavaScript 中的 camelCase
const BlogPost = {
    
    
  props: ['postTitle'],
  emits: ['updatePost'],
  template: `
    <h3>{
     
     { postTitle }}</h3>
  `
}
<!-- HTML 中的 kebab-case -->
<blog-post post-title="hello!" @update-post="onUpdatePost"></blog-post>

Closing tags {#self-closing-tags}

We already used the self-closing tag in the example above:

<MyComponent />

This is because Vue's template parser supports the use of arbitrary tags />as a tag-closing marker.

In DOM templates, however, we must explicitly write the closing tag:

<my-component></my-component>

This is because HTML only allows a small number of special elements to omit their closing tags, the most common being <input>and <img>. For other elements, if you omit the closing tag, the native HTML parser will think that the opening tag will never end. Take the following code snippet as an example:

<my-component /> <!-- 我们想要在这里关闭标签... -->
<span>hello</span>

will be parsed as:

<my-component>
  <span>hello</span>
</my-component> <!-- 但浏览器会在这里关闭标签 -->

Element placement restrictions {#element-placement-restrictions}

Certain HTML elements have restrictions on the types of elements placed inside them, eg <ul>, <ol>, <table>and <select>, and accordingly, certain elements are only displayed when placed inside specific elements, eg <li>, <tr>and <option>.

This will cause problems when using components with such restricted elements. For example:

<table>
  <blog-post-row></blog-post-row>
</table>

Custom components <blog-post-row>will be ignored as invalid content, thus causing errors in the final rendered output. We can use special isattributes as a solution:

<table>
  <tr is="vue:blog-post-row"></tr>
</table>

When :::tip
is used on a native HTML element, isthe value must be prefixed with vue:to be parsed as a Vue component. This is necessary to avoid confusion with native custom built-in elements .
:::

The above is all the considerations you need to know about DOM template parsing, and it is also all the content of the basic part of Vue. congratulations! While there's still a lot to learn, you can pause for a moment and go do something interesting with Vue, or study some examples .

After reading this page, review what you just learned, and if you want to know more details, we recommend you continue to read the full guide on components.

Slots Slots {#slots}

This chapter assumes that you have looked at Component Fundamentals . If you don't know what a component is yet, please read this chapter first.

Slot content and outlet {#slot-content-and-outlet}

In the previous chapters, we have seen that components can receive any type of JavaScript value as props, but how do components receive template content? In some scenarios, we may want to pass some template fragments to the child components and let the child components render these fragments in their components.

For example, here's a <FancyButton>component that can be used like this:

<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

And <FancyButton>the template for is this:

<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
</button>

<slot>The element is a slot outlet , indicating where the slot content provided by the parent element will be rendered.

Slot Diagram

The final rendered DOM looks like this:

<button class="fancy-btn">Click me!</button>

Try it out in the playground

Try it out in the playground

By using slots, <FancyButton>only the outer layer <button>(and corresponding styles) are responsible for rendering, while the inner content is provided by the parent component.

Another way to think about slots is as an analogy to the following JavaScript function, which is conceptually similar:

// 父元素传入插槽内容
FancyButton('Click me!')

// FancyButton 在自己的模板中渲染插槽内容
function FancyButton(slotContent) {
    
    
  return `<button class="fancy-btn">
      ${
      
      slotContent}
    </button>`
}

Slot content can be any legal template content, not limited to text. For example, we can pass in multiple elements, or even components:

<FancyButton>
  <span style="color:red">Click me!</span>
  <AwesomeIcon name="plus" />
</FancyButton>

Try it out in the playground

Try it out in the playground

<FancyButton>Components are more flexible and reusable by using sockets . Now components can be used in different places to render different content, but at the same time ensure that they all have the same style.

The slot mechanism of the Vue component was inspired by the native Web Component <slot>elements , and at the same time, some function extensions have been made, which we will learn later.

render scope {#render-scope}

Slot content has access to the parent component's data scope because the slot content itself is defined in the parent component's template. for example:

<span>{
   
   { message }}</span>
<FancyButton>{
   
   { message }}</FancyButton>

{ { message }}The content rendered by the two interpolation expressions here is the same.

Slot contents cannot access data of child components. Expressions in Vue templates can only access the scope in which they were defined, which is consistent with JavaScript's lexical scoping rules. In other words:

Expressions in parent component templates can only access the parent component's scope; expressions in child component templates can only access the child component's scope.

default content {#fallback-content}

It is possible to specify a default content for a slot in case nothing is provided externally. For example, there is such a <SubmitButton>component:

<button type="submit">
  <slot></slot>
</button>

<button>If we want to render "Submit" inside when the parent component doesn't provide any slot content , we just need to write "Submit" <slot>between the tags as the default content:

<button type="submit">
  <slot>
    Submit <!-- 默认内容 -->
  </slot>
</button>

Now, when we use in a parent component <SubmitButton>without providing any slot content:

<SubmitButton />

"Submit" will be rendered as the default content:

<button type="submit">Submit</button>

But if we provide the slot content:

<SubmitButton>Save</SubmitButton>

Then the explicitly provided content will override the default content:

<button type="submit">Save</button>

Try it out in the playground

Try it out in the playground

named slots {#named-slots}

Sometimes it is useful to have multiple socket outlets in a component. For example, in a <BaseLayout>component with the following template:

<div class="container">
  <header>
    <!-- 标题内容放这里 -->
  </header>
  <main>
    <!-- 主要内容放这里 -->
  </main>
  <footer>
    <!-- 底部内容放这里 -->
  </footer>
</div>

For this kind of scenario, <slot>the element can have a special attribute name, which is used to assign a unique ID to each slot to determine the content to be rendered in each place:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

Such nameslots are called named slots. Exports that are not provided nameare <slot>implicitly named "default".

When using in a parent component <BaseLayout>, we need a way to pass the contents of multiple slots into the outlets of their respective target slots. At this point you need to use named slots :

To pass in content for a named slot, we need to use an element with a v-slotdirective <template>and pass it the name of the target slot:

<BaseLayout>
  <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
  </template>
</BaseLayout>

v-slotThere is a corresponding abbreviation #, so <template v-slot:header>can be abbreviated as <template #header>. It means "pass this part of the template fragment into the header slot of the child component".

named socket icon

Below we give the complete <BaseLayout>code for passing the content of the slot to , and the instructions are all in abbreviated form:

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <template #default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

When a component receives both default and named slots, all top-level non- <template>nodes are implicitly considered the contents of the default slot. So the above can also be written as:

<BaseLayout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <!-- 隐式的默认插槽 -->
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</BaseLayout>

Now <template>everything in the element will be passed to the corresponding slot. The final rendered HTML is as follows:

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

Try it out in the playground

Try it out in the playground

It may be more helpful to understand named slots using the JavaScript function analogy:

// 传入不同的内容给不同名字的插槽
BaseLayout({
    
    
  header: `...`,
  default: `...`,
  footer: `...`
})

// <BaseLayout> 渲染插槽内容到对应位置
function BaseLayout(slots) {
    
    
  return `<div class="container">
      <header>${
      
      slots.header}</header>
      <main>${
      
      slots.default}</main>
      <footer>${
      
      slots.footer}</footer>
    </div>`
}

Dynamic slot names {#dynamic-slot-names}

Dynamic directive parametersv-slot are also valid on , i.e. you can define dynamic slot names like this :

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>

  <!-- 缩写为 -->
  <template #[dynamicSlotName]>
    ...
  </template>
</base-layout>

Note that expressions here are subject to the same syntax restrictions as dynamic directive parameters .

scoped slots {#scoped-slots}

As we discussed in the rendering scope above , the content of the slot cannot access the state of the child component.

However in some scenarios the content of the slot may want to use data from both the parent component's scope and the child component's scope. To do this, we need a way for the child component to provide a portion of the data to the slot when it renders.

We do have a way to do that, too! Attributes can be passed on the exit of a slot just like props are passed to components:

<!-- <MyComponent> 的模板 -->
<div>
  <slot :text="greetingMessage" :count="1"></slot>
</div>

There is a small difference in how default slots and named slots are used when receiving slot props. v-slotBelow we will first show how the default slot accepts props, and directly receives a slot props object through the directive on the subcomponent tag :

<MyComponent v-slot="slotProps">
  {
   
   { slotProps.text }} {
   
   { slotProps.count }}
</MyComponent>

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-Lq0fLQB4-1691855652867)(https://cn.vuejs.org/assets/scoped-slots.1c6d5876.svg )]

Try it out in the playground

Try it out in the playground

The props passed into the slot by the child component are used as v-slotthe value of the directive, which can be accessed in the expression inside the slot.

You can think of a scoped slot as a function passed into a child component. The child component will pass the corresponding props as parameters to it:

MyComponent({
    
    
  // 类比默认插槽,将其想成一个函数
  default: (slotProps) => {
    
    
    return `${
      
      slotProps.text} ${
      
      slotProps.count}`
  }
})

function MyComponent(slots) {
    
    
  const greetingMessage = 'hello'
  return `<div>${
      
      
    // 在插槽函数调用时传入 props
    slots.default({
      
       text: greetingMessage, count: 1 })
  }</div>`
}

In fact, this is already very similar to the final code compilation result of scoped slots, and the way scoped slots are used when manually writing render functions .

v-slot="slotProps"It can be compared to the function signature here, similar to the parameters of the function, we can also v-slotuse destructuring in:

<MyComponent v-slot="{ text, count }">
  {
   
   { text }} {
   
   { count }}
</MyComponent>

Named scoped slots {#named-scoped-slots}

Named scope slots work similarly, slot props can v-slotbe accessed as the value of the directive: v-slot:name="slotProps". When using abbreviations like this:

<MyComponent>
  <template #header="headerProps">
    {
   
   { headerProps }}
  </template>

  <template #default="defaultProps">
    {
   
   { defaultProps }}
  </template>

  <template #footer="footerProps">
    {
   
   { footerProps }}
  </template>
</MyComponent>

Pass props to named slots:

<slot name="header" message="hello"></slot>

Note that on the slot nameis a special reserved attribute of Vue and will not be passed to the slot as props. So the end headerPropsresult is { message: 'hello' }.

If you use both named and default slots, you need to use an explicit <template>tag for the default slot. Attempting to add a directive directly to a component v-slotwill result in a compilation error. This is to avoid confusion about the scope of props for default slots. Example:

<!-- 该模板无法编译 -->
<template>
  <MyComponent v-slot="{ message }">
    <p>{
   
   { message }}</p>
    <template #footer>
      <!-- message 属于默认插槽,此处不可用 -->
      <p>{
   
   { message }}</p>
    </template>
  </MyComponent>
</template>

Using an explicit <template>tag for the default slot helps to more clearly indicate that messagethe attribute is not available in other slots:

<template>
  <MyComponent>
    <!-- 使用显式的默认插槽 -->
    <template #default="{ message }">
      <p>{
   
   { message }}</p>
    </template>

    <template #footer>
      <p>Here's some contact info</p>
    </template>
  </MyComponent>
</template>

Advanced List Component Example {#fancy-list-example}

You may want to ask what kind of scenarios are suitable for using scoped slots. Here we look at an <FancyList>example of a component. It renders a list, and at the same time encapsulates some logic for loading remote data, using data for list rendering, or more advanced functions like pagination or infinite scrolling. However, we want it to remain flexible enough to leave control over the content and styling of individual list elements to the parent component that uses it. Our desired usage might be something like this:

<FancyList :api-url="url" :per-page="10">
  <template #item="{ body, username, likes }">
    <div class="item">
      <p>{
   
   { body }}</p>
      <p>by {
   
   { username }} | {
   
   { likes }} likes</p>
    </div>
  </template>
</FancyList>

In <FancyList>, we can render multiple times <slot>and provide different data each time (note that we use here v-bindto pass the slot's props):

<ul>
  <li v-for="item in items">
    <slot name="item" v-bind="item"></slot>
  </li>
</ul>

Try it out in the playground

Try it out in the playground

Renderless components {#renderless-components}

The above <FancyList>case encapsulates reusable logic (data acquisition, paging, etc.) and view output at the same time, but part of the view output is also handed over to the consumer component to manage through the scope slot.

If we expand this concept, it is conceivable that some components may only include logic and do not need to render content by themselves, and the view output is fully handed over to the consumer component through the scope slot. We refer to this type of component as a renderless component .

Here's an example of a renderless component, a component that encapsulates logic for tracking the current mouse position:

<MouseTracker v-slot="{ x, y }">
  Mouse is at: {
   
   { x }}, {
   
   { y }}
</MouseTracker>

Try it out in the playground

Try it out in the playground

While this pattern is interesting, most of the functionality that can be achieved with renderless components can be achieved in another, more efficient way through the composition API, without incurring the overhead of additional component nesting. Later, we will introduce how to implement the function of tracking the mouse position more efficiently in the chapter of combined functions .

Still, scoped slots are useful when you need to encapsulate logic while composing view interfaces, like the<FancyList> components above.

Guess you like

Origin blog.csdn.net/qq_32907491/article/details/132255206