1, the slot

(1) Slot content

Like HTML elements, we often need to pass content to components

For example, in the example below, we pass the error text to the custom component prompt-info

<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <prompt-info>Error!</prompt-info>
    </div>

    <script>
        Vue.component('prompt-info', {
            template: '
       

Prompt

'
}) new Vue({ el: '#app' })
</script> </body> </html> Copy the code

However, as expected, the error message is not displayed, so we can use

to solve the problem


is a slot, which means we need to leave a place in the component before we can put the content passed in for use

Vue.component('prompt-info', {
    template: '
      
< H3 > Prompt
'
}) Copy the code

We can also provide default content for the slot, but it will only be displayed when no content is provided

Vue.component('prompt-info', {
    template: '
      
< H3 > Prompt Sorry
'
}) Copy the code

Once the slot is defined, the incoming content is displayed normally. In fact, we can pass in not only simple text, but also any template code

<div id="app">
    <prompt-info>
        <span>Error!</span>
        <p>Please try again later</p>
    </prompt-info>
</div>
Copy the code

(2) Slot data

Consider that the template code we passed above contains no data. What happens if we use data now?

There are certain considerations

  • Data is defined in the parent template and used in the parent template: for example, MSG below
<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <prompt-info>{{ msg }}</prompt-info>
    </div>

    <script>
        Vue.component('prompt-info', {
            template: `
                <div>
                    <h3>Prompt information</h3>
                    <slot></slot>
                </div>`}) new Vue ({el: '# app, data: {MSG:' server ran away from home! T_T '}})</script>
</body>

</html>
Copy the code
  • The data is defined in the subtemplate and used in the subtemplate: for example, the following info
<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <prompt-info></prompt-info>
    </div>

    <script>Vue.component('prompt-info', {data: function () {return {info: 'sorry, please try again'}}, template: '<div>
                    <h3>Prompt information</h3>
                    <slot>{{ info }}</slot>
                </div>
            `
        })
        new Vue({
            el: '#app'
        })
    </script>
</body>

</html>
Copy the code
  • Data is defined in child templates and used in parent templates

This time it’s not so simple. Let’s look at a simple code:

<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <prompt-info>{{ info }}</prompt-info>
    </div>

    <script>
        Vue.component('prompt-info', {
            data: function () {
                return {
                    info: 'Sorry, please try again.'}},template: '
       
< H3 > Prompt
'
}) new Vue({ el: '#app' })
</script> </body> </html> Copy the code

In fact, the code will not compile correctly, and the console will print an error message: info is not defined

This is because everything in the parent template is compiled in the parent scope, and everything in the child template is compiled in the child scope

What if we want the slot content to access the child component data?

First, we need to bind the data on the

element using the V-bind instruction (the feature bound to the

element is called slot prop)

We can then get and use the slot prop on the component using the V-slot directive

(Note that the syntax for using v-slot here is v-slot=’slotProps’)

<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <prompt-info v-slot='slotProps'>{{ slotProps.info }}</prompt-info>
    </div>

    <script>Vue.component('prompt-info', {data: function () {return {defaultInfo: 'Sorry, please try again ', info: 'Server run away T_T'}}, template:'<div>
                    <h3>Prompt information</h3>
                    <slot v-bind:info='info'>{{ defaultInfo }}</slot>
                </div>
            `
        })
        new Vue({
            el: '#app'
        })
    </script>
</body>

</html>
Copy the code

(3) Named slot

Ok, now let’s think about another problem, when we need to use multiple slots, how do we differentiate between them?

The answer is to use the name feature to name slots when defined, and then use the V-slot directive to specify slots when used

(Note that the syntax for using v-slot is V-slot :slotName, which does not conflict with the above syntax.)

<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <prompt-info>
            <template v-slot:header>
                <p>The service ran away from home</p>
            </template>
			
            <! -- Elements not specified by the v-slot directive are placed in the default slot -->
            <template>
                <p>Please try again later</p>
            </template>

            <template v-slot:footer>
                <p>I'm sorry</p>
            </template>
        </prompt-info>
    </div>

    <script>
        Vue.component('prompt-info', {
            template: `
                <div>
                    <h3>Prompt information</h3>
                    <slot name="header"></slot>
					<! -- Slots that are not named using the name feature default with the name default -->
                    <slot></slot> 
                    <slot name="footer"></slot>
                </div>
            `
        })
        new Vue({
            el: '#app'
        })
    </script>
</body>

</html>
Copy the code
  • 4. Abbreviation for named slot:

Like the V-ON and V-bind directives, Vue provides an abbreviation for the V-slot directives

<! -- Complete syntax -->
<template v-slot:header>.</template>

<! - for - >
<template #header>.</template>
Copy the code
  • Abbreviation for exclusive default slot:

When there is only one default slot, we can use v-slot directly on components:

Vue.component('prompt-info', {
    template: `
        <div>
            <h3>Prompt information</h3>
            <slot></slot> 
        </div>`})Copy the code
<prompt-info v-slot:default="slotProps"></prompt-info>
Copy the code

But whenever multiple slots are present, always use the full

Vue.component('prompt-info', {
    template: `
        <div>
            <h3>Prompt information</h3>
            <slot name="header"></slot>
            <slot></slot> 
            <slot name="footer"></slot>
        </div>`})Copy the code
<prompt-info v-slot:default="slotProps">
    <template v-slot:header="slotProps">
    	<! - content - >
    </template>
    <template v-slot:default="slotProps">
    	<! - content - >
    </template>
    <template v-slot:footer="slotProps">
    	<! - content - >
    </template>
</prompt-info>
Copy the code

Dynamic components & Asynchronous components

(1) Dynamic components

A dynamic component is a dynamically switched component, which can be implemented using the < Component > element in conjunction with the IS feature. See a simple example

<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <button v-for="tab in tabs" v-on:click="currentTab = tab">{{ tab }}</button>
        <component v-bind:is="currentTabComponent"></component>
    </div>

    <script>
        Vue.component('tab-home', {
            template: '<div>Home component</div>'
        })
        Vue.component('tab-post', {
            template: '<div>Posts component</div>'
        })
        Vue.component('tab-archive', {
            template: '<div>Archive component</div>'
        })

        new Vue({
            el: '#app'.data: {
                currentTab: 'Home'.tabs: ['Home'.'Post'.'Archive']},computed: {
                currentTabComponent: function () {
                    return 'tab-' + this.currentTab.toLowerCase()
                }
            }
        })
    </script>
</body>

</html>
Copy the code

Vue creates an instance of currentTabComponent each time a component is switched

However, there are times when we want to preserve the state of the component to avoid performance issues caused by repeated rerendering

This can be done with the

element, in which the components wrapped are cached for the first creation

<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>
Copy the code

(2) Asynchronous components

An asynchronous component is a component that is loaded asynchronously. We can define a component using a factory function. A simple example is shown below

<! DOCTYPE html><html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <p>Hello</p>
        <my-component></my-component>
    </div>

    <script>
        Vue.component('my-component'.function (resolve, reject) {
            // The timeout function is used to simulate network latency
            setTimeout(function () {
                resolve({
                    template: '
        
I am async!
'
})},1000)})new Vue({ el: '#app' })
</script> </body> </html>
Copy the code

We can also use webPack and ES2015 syntax together by returning a Promise object in the factory function

Vue.component('my-component'.() = > import('./async-component'))
Copy the code

3. Boundary conditions

(1) Access elements

Most of the time, you shouldn’t go inside another component instance or manually manipulate DOM elements, but it does happen

  • Access the parent component instance
<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <my-component></my-component>
    </div>

    <script>
        Vue.component('my-component', {
            template: `<h3>{{ this.$parent.msg }}</h3>`
        })
        new Vue({
            el: '#app'.data: {
                msg: 'hello'}})</script>
</body>

</html>
Copy the code
  • Access child component instances
<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <button v-on:click="showMsg()">The print information</button>
        <my-component ref="page"></my-component>
    </div>

    <script>
        Vue.component('my-component', {
            template: `<p>Hello World</p>`
        })
        new Vue({
            el: '#app'.methods: {
                showMsg: function () {
                    console.log(this.$refs.page)
                }
            }
        })
    </script>
</body>

</html>
Copy the code
  • Accessing child elements
<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <button v-on:click="changeText()">Change the text</button>
        <p ref="page">Hello World</p>
    </div>

    <script>
        new Vue({
            el: '#app'.methods: {
                changeText: function () {
                    this.$refs.page.innerHTML = 'Hello Vue'}}})</script>
</body>

</html>
Copy the code
  • Dependency injection

Use the provide option to specify what data to provide to descendants, and then use the Inject option to receive data in any descendant component

<! DOCTYPEhtml>
<html>

<head>
    <title>Demo</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>

<body>
    <div id="app">
        <my-component></my-component>
    </div>

    <script>
        Vue.component('my-component', {
            inject: ['msg'.'toLower'].template: `<p>{{ toLower(msg) }}</p>`
        })
        new Vue({
            el: '#app'.data: {
                msg: 'HELLO'
            },
            methods: {
                toLower: function (msg) {
                    return msg.toLowerCase()
                }
            },
            provide: function () {
                return {
                    msg: this.msg,
                    toLower: this.toLower
                }
            }
        })
    </script>
</body>

</html>
Copy the code