The recently released Vue 2.6 has become more concise in the syntax of using slots. This change to slots has made me interested in discovering the potential features of slots in order to provide reusability, new features and clearer readability for our Vue-based projects. What are the slots that are truly capable?
If you're new to Vue, or haven't seen the changes in version 2.6, please continue reading. Perhaps the best resource for learning slots is Vue's own documentation, but I'll give an outline here.
If you want to read more high-quality articles, pleaseClick on the GitHub blog, More than a hundred high-quality articles are waiting for you in a year!
What is a slot?
Slots are a mechanism for Vue components that allow you to combine components in a different way than a strict parent-child relationship. The slot provides you with an outlet to place content in a new location or make the component more versatile. Start with a simple example:
// <template> <div class="frame"> <slot></slot> </div> </template>
The outermost layer of this component is a div. Suppose the existence of a div is to create a style framework around its content. This component can be used to surround the framework on whatever you want to wq to see how it works. The frame component here refers to the component we just made.
// <template> <frame><img src=""></frame> </template>
The content between the start and end frame tags is inserted into the frame component where the slot is located, replacing the slot tag. This is the most basic method. It is also possible to simply specify the default content to be placed in the slot by filling it
// <template> <div class="frame"> <slot>If nothing is specified here,This is the default content</slot> </div> </template>
So now if we use it like this:
// <template> <frame /> </template>
"If nothing is specified here, this is the default content" is the default content, but if you use it like before, the default text will be overwritten by the img tag.
Multiple/named slots
Multiple slots can be added to the component, but if you do this, then all other slots need to have names except one of them. If there is a slot without a name, it is the default slot. Here's how to create multiple slots:
// <template> <div class="frame"> <header><h2> <slot name="header">Title</slot> </h2></header> <slot>If nothing is specified here,This is the default content</slot> </div> </template>
We keep the same default slot, but this time we add a slot called header where you can enter the title, using it as follows:
// <template> <titled-frame> <template v-slot:header> <!-- The code below goes into the header slot --> My Image's Title </template> <!-- The code below goes into the default slot --> <img src=""> </titled-frame> </template>
Just like before, if we want to add content to the default slot, just put it directly in the titled-frame component. However, to add content to the named slot, we need to wrap the code in the template tag with the v-slot directive. Add a colon (:) after v-slot and write out the name of the slot to which you want to pass.
Note that v-slot is the new version of Vue 2.6, so if you are using an older version, you need to read the documentation on the deprecated slot syntax.
Scope slots
Another thing you need to know is that slots can pass data/functions to their children. To prove this, we need a completely different example component with slots: create a component that feeds the current user's data to its slots:
// <template> <span> <slot v-bind:user="user"> {{ }} </slot> </span> </template> <script> export default { data () { return { user: ... } } } </script>
The component has a property called user that contains detailed information about the user. By default, the component displays the user's last name, but note that it uses v-bind to bind user data to slot . This way, we can use this component to provide user data to its descendants
// <template> <current-user> <template v-slot:default="slotProps">{{ }}</template> </current-user> </template>
To access the data passed to slot, we use the value of the v-slot directive to specify the name of the scope variable.
Here are a few points to note:
- We have specified the name of default, but we do not need to specify a name for the default slot. Instead, we can use
v -slot="slotProps"
。 - You don't need to use slotProps as your name, you can call it whatever you want.
- If you only use the default slot, you can skip the internal template tag and directly place the v-slot directive on the current current-user.
- Instead of using a single variable name, you can use object destruction to create a direct reference to scoped slot data. In other words, it can be used
v-slot="{user}"
replacev-slot="slotProps
" , then you can use user directly instead of .
So, the above example can be rewritten like this
// <template> <current-user v-slot="{user}"> {{ }} </current-user> </template>
There are a few more things to remember:
- Multiple values can be bound using the v-bind directive.
- Functions can also be passed to scope slots. Many libraries use it to provide reusable functional components.
- The alias for v-slot is # . Therefore, you can use #header="data" instead of v-slot:header="data" . You can also use #header instead of v-slot:header (prerequisite: when not a scope slot). For default slots, you need to specify the default name when using an alias. In other words, you need to write #default="data" instead of #="data" like this.
CandocumentLearn more about the details in this article, but this is enough to help you understand what is discussed in the rest of this article.
What can you do with slots?
Slots are not built for one purpose, or at least if they are, they have surpassed their original intentions and become powerful tools for doing many different things.
Reusable mode
Components are always designed to be reusable, but some patterns are not practical for implementing using a single "normal" component, because in order to customize it, the number of props required may be too high or most of the content or other components need to be passed through props.
The slot can be wrapped with external HTML tags or components, and allows other HTML or components to be placed on the slot with the corresponding name of the named slot.
For the first example, start with something simple: a button. Suppose our team is using Bootstrap. With Bootstrap, buttons are usually bound to the basic "btn" class and the class with the specified color, such as "btn-primary
” . You can also add size class, such as 'btn-lg' .
For simplicity, now let's assume your application usesbtn , btn-primary and btn-lg
. You don't want to always have to write these three classes on the button, or you don't believe newbies will remember to write these three classes.
In this case, it is possible to create a component that automatically contains all three classes, but how can custom content be allowed? prop is not practical because the buttons are allowed to contain various HTML, so we should use a slot.
<!-- --> <template> <button class="btn btn-primary btn-lg"> <slot>Click Me!</slot> </button> </template>
Now we can use it anywhere, no matter what content you want
<!-- use --> <template> <my-button> <img src="/img/"> I'm Xiaozhi! </my-button> </template>
Of course, you can choose something bigger than the button. Stick to Bootstrap, let's look at a modal:
<!-- --> <template> <div class="modal" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <slot name="header"></slot> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <slot name="body"></slot> </div> <div class="modal-footer"> <slot name="footer"></slot> </div> </div> </div> </div> </template>
Now, use it:
<!-- use --> <template> <my-modal> <template #header> <h5>Everyone's the best!</h5> </template> <template #body> <p>Come on, everyone</p> </template> <template #footer> <em>Everyone is very good!</em> </template> </my-modal> </template>
The slot use cases of the above type are obviously very useful, but it can do more.
Multiplexing functions
Vue components are not entirely about HTML and CSS. They are built in JavaScript, so they are also about functions. Slots are useful for creating functions at once and using functions in multiple places. Let's go back to the modal example and add a function that closes the modal
<!-- --> <template> <div class="modal" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <slot name="header"></slot> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <slot name="body"></slot> </div> <div class="modal-footer"> <slot name="footer" :closeModal="closeModal"></slot> </div> </div> </div> </div> </template> <script> export default { //... methods: { closeModal () { // Things to do when closing the dialog box } } } </script>
When using this component, you can add a button to footer that can turn off modality. Generally, in the case of Bootstrap mode, it is possible todata-dismiss =“modal”
Add to button to close.
But we want to hide something specific to Bootstrap. So we pass them a function they can call so that the user won't know that we have something to use Bootstrap.
<!-- use --> <template> <my-modal> <template #header> <h5>Awesome Interruption!</h5> </template> <template #body> <p>Come on, everyone!</p> </template> <template #footer="{closeModal}"> <button @click="closeModal"> Click I can close the annoying dialog box </button> </template> </my-modal> </template>
No rendering component
Finally, you can take advantage of what you know about using slots to pass reusable functions and strip away all HTML and use only slots. This is the essence of a render-free component: a component that only provides functions without any HTML.
Making a component truly render-free can be a bit tricky, as it requires writing a render function instead of using a template to remove dependency on the root element, but it may not always be necessary. Let’s take a look at a simple example of using the template first:
<template> <transition name="fade" v-bind="$attrs" v-on="$listeners"> <slot></slot> </transition> </template> <style> .fade-enter-active, .fade-leave-active { transition: opacity 0.3s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
This is a weird example of no rendering components, as it doesn't even have any JavaScript. This is mainly because we are creating a preconfigured reusable version with built-in rendering function: transition .
Yes, Vue has built-in render-free components. This particular example is taken from a Cristi Jora article about reusable transitions, showing an easy way to create a render-free component that can standardize transitions used throughout the application.
For another example we will create a component to handle the content displayed in different states of the switch Promise: pending, resolved, and failed. This is a common pattern, and while it doesn't require a lot of code, it can mess up a lot of components without extracting logic for reusability.
<!-- --> <template> <span> <slot name="rejected" v-if="error" :error="error"></slot> <slot name="resolved" v-else-if="resolved" :data="data"></slot> <slot name="pending" v-else></slot> </span> </template> <script> export default { props: { promise: Promise }, data: () => ({ resolved: false, data: null, error: null }), watch: { promise: { handler (promise) { = false = null if (!promise) { = null return } (data => { = data = true }) .catch(err => { = err = true }) }, immediate: true } } } </script>
What's going on, little brother? First of all, please note that this component receives a Promise type parameter. In the watch section, listen for changes in promise, clear the state when the promise changes, then call then and catch promise, and update the state when the promise completes successfully or fails.
Then, in the template, we display a different slot according to the status. Note that we did not keep it truly render-free because we need a root element to use the template. We also pass data and error to the relevant slot range.
<template> <div> <promised :promise="somePromise"> <template #resolved="{ data }"> Resolved: {{ data }} </template> <template #rejected="{ error }"> Rejected: {{ error }} </template> <template #pending> Requesting... </template> </promised> </div> </template> ...
We pass somePromise to the renderless component. Then wait for it to complete, and for the pending slot, "Requesting..." is displayed. If successful, "Resolved: the corresponding value" is displayed. If it fails, "Rejected: Result of the failure". Now we no longer need to track the status of promises in this component, because the part is pulled out into its own reusable component.
So, what can we do to bypass the slots in ? To delete it, we need to delete the template section and add the render function to our component:
render () { if () { return this.$scopedSlots['rejected']({error: }) } if () { return this.$scopedSlots['resolved']({data: }) } return this.$scopedSlots['pending']() }
There is nothing too complicated here. We just use some if blocks to find the state, then return the correct scope slot (via this.$ scopedslot ['SLOTNAME'](...) ) and pass the relevant data to the slot scope. When you are not using templates, you can skip using the .vue file extension by extracting JavaScript from the script tag and putting it into the .js file. This should give you a very small performance boost when compiling these Vue files.
Summarize
Vue's slots take component-based development to a whole new level, and while this article has shown many great ways to use slots, there are many more.
The above is the new usage of slot in vue 2.6 introduced to you by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support for my website!
If you think this article is helpful to you, please reprint it. Please indicate the source, thank you!