Vue uses slots to distribute content

1. Introduction

  components are used as user-defined elements. Elements can have attributes and contents. Receiving attribute values through prop defined by components can solve attribute problems. What about contents? This can be solved by the < slot > element. In addition, slots can also be used as another implementation of communication between parent and child components.
  here is a simple custom component.

Vue.component('greeting',{
	template:'<div><slot></slot></div>'
})

    in the component template, a < slot > element is used inside the < div > element, which can be understood as a placeholder.
The code for using this component is as follows:

<greeting>Hello,Vue.js</greeting>

The     < greeting > element gives the content, which will replace the < slot > element inside the component when rendering the component. The final rendering result is as follows:

<div>Hello,Vue.js</div>

2. Compile scope

  if you want to transfer dynamic data to the component through the slot. For example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <greeting>Hello {{name}}</greeting>
    </div>
    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const app=Vue.createApp({ });
        app.component('greeting',{
            data(){
                return{
                    name:'zhangsan'
                }
            },
            template:'<div><slot>Hello Java</slot></div>'
        })
        app.mount('#app');
    </script>
</body>
</html>

    it should be clear that name is resolved under the scope of the parent component, not under the scope of the meeting component. In other words, the name data attribute defined inside the meeting component cannot be accessed here. The name must be in the data option of the parent component. This is the problem of compilation scope.
  in short, all contents in the parent component template are compiled within the parent scope; Everything in the sub component template is compiled within the sub scope.
The correct code is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <greeting>Hello {{name}}</greeting>
    </div>
    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const app=Vue.createApp({
            data(){
                return{
                    name:'lisi'
                }
            }
        });
        app.component('greeting',{
            template:'<div><slot>Hello Java</slot></div>'
        })
        app.mount('#app');
    </script>
</body>
</html>

Render results:

3. Default content

    when using the < slot > element inside the component, you can specify a default content for the element to prevent the user of the component from not passing content to the component. For example, the template content of a component < submit button > used to submit a button is as follows:

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

   use < submit button > in the parent component, but do not provide slot content. The code is as follows:

<submit-button></submit-button>

   then the rendering result of this component is as follows:

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

  if the parent component provides slot content, the code is as follows:

<submit-button>register</submit-button>

  then the rendering result of the component is as follows:

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

4. Named slot

  multiple slots may be required when developing components< The slot > element has a name attribute, which can define multiple slots. The < slot > element that does not use the name attribute has the implied name default.
    when providing content to a named slot, use the v-slot instruction on a < template > element and specify the name of the slot in the form of a v-slot parameter.
The code is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"><!-- available#Replace V-slot: -- >
        <base-layout>
            <template #header>
                <h1>This is the header</h1>
            </template>
            <template v-slot:default>
                <section>This is a text paragraph</section>
                <section>Another body paragraph</section>
            </template>
            <template v-slot:footer>
                <h3>This is the footer</h3>
            </template>
        </base-layout>
    </div>
    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const app=Vue.createApp({});
        app.component('base-layout',{
            template:`
                <div>
                    <header><slot name="header"></slot></header>
                    <main><slot></slot></main>
                    <footer><slot name="footer"></slot></footer>    
                </div>
            `
        });
        app.mount('#app');

    </script>
</body>
</html>

Render results:

  note that the v-slot instruction can only be used on < template > elements or component elements. There are some restrictions on the use of component elements, as shown in the next section.
Like the v-bind and v-on instructions, the v-slot instruction also has an abbreviated syntax, that is, replace "v-slot" with "#".

5. Scope slot

    as mentioned earlier, under the parent scope, the data attributes of child components cannot be accessed in the slot content, but sometimes the data of child components need to be accessed in the slot content of the parent. Therefore, you can bind a prop on the < slot > element of the child component with the v-bind instruction. The code is as follows:

app.component('MyButton',{
            data(){
                return{
                    titles:{
                        login:'Sign in',
                        register:'register'
                    }
                }
            },
            template:`
                <label><slot name="other" :label="titles"> {{labels.login}}</slot></label>
                <button>
                    <slot :values="titles">
                        {{titles.login}}
                    </slot>    
                </button>
            `
        });

    the name of this button can be switched between "login" and "registration". In order to enable the parent component to access titles, bind a values attribute on the < slot > element with the v-bind instruction to become a slot prop. This prop does not need to be declared in the props option.
  when using this component under the parent scope, you can give the v-slot instruction a value to define the name of the slot prop provided by the component. The code is as follows:

<my-button>
            <template v-slot:default="slotProps">
                {{slotProps.values.register}}
            </template>
 </my-button>

    because the slot in the < my button > component is the default slot, the implicit name default is used here, and then a name slotProps is given. This name can be taken arbitrarily, which represents an object containing all slot props in the component. Then you can use this object to access the component's slot prop. Values prop is bound to the title data attribute, Therefore, you can further access the contents of titles. The final rendering result is: < button > register < / button >
    if there are multiple slots, you should always use the complete < template > based syntax for all slots. The code is as follows:

 <my-button>
        <template v-slot:default="slotProps">
                {{slotProps.values.register}}
        </template>
        <template v-slot:other="otherProps">
            {{otherProps.label.register}}
        </template> 
    </my-button>

   the internal working principle of the scope slot is to wrap the slot content into a function passing a single parameter. The code is as follows:

function(slotProps){
	//Slot content
}

  this means that v-slot can actually be any JavaScript expression that can be used as a function definition parameter. Therefore, in the environment supporting ES6, you can use the deconstruction syntax to extract a specific slot prop. You can also rename the slot prop when extracting it. The code is as follows:

<my-button>
        <template v-slot:default="{values:titles}"><!-- rename -->
            {{titles.register}}
         </template>
         <template v-slot:other="{label}">
             {{label.register}}
          </template>
    </my-button>

6. Dynamic slot name

   the dynamic instruction parameter can also be used on the v-slot instruction to define the dynamic slot name. The code is as follows:

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

   dynamicSlotName needs to be able to resolve normally under the parent scope, such as corresponding data attributes or calculation attributes. If it is used in the DOM template, you should also pay attention to the case of element attribute names.

Keywords: Vue Vue.js

Added by imartin on Sun, 16 Jan 2022 08:17:49 +0200