Slots Vue 3
While basic component slots are all fine and dandy, sometimes you want the template inside the slot to be able to access data from the child component hosting the slot content. For example, if you’re trying to allow custom templates in a container while still retaining access to those containers’ data properties, you’ll want to use a. Vue-3 props, $emit, slot, render, JSX and createElement Props and $emit. When using Vue to develop a project, we divide the content of the project into modules, but sometimes there is data interaction between modules. In real project development, father-son and brother components need to pass values to each other. Contribute to vuejs/jsx-next development by creating an account on GitHub. And v-slots is still available even if enableObjectSlots is turned off.
Vue 3 has not been officially released yet, but the maintainers have released beta versions for us punters to try and provide feedback on.
If you're wondering what the key features and main changes of Vue 3 are, I'll highlight them in this article by walking you through the creation of a simple app using Vue 3 beta 9.
I'm going to cover as much new stuff as I can including fragments, teleport, the Composition API, and several more obscure changes. I'll do my best to explain the rationale for the feature or change as well.
What we'll build
We're going to build a simple app with a modal window feature. I chose this because it conveniently allows me to showcase a number of Vue 3 changes.
Here's what the app looks like in it's opened and closed states so you can picture in your mind what we're working on:
Vue 3 installation and setup
Rather than installing Vue 3 directly, let's clone the project vue-next-webpack-preview
which will give us a minimal Webpack setup including Vue 3.
Once that's cloned and the NPM modules are installed, all we need to do is remove the boilerplate files and create a fresh main.js
file so we can create our Vue 3 app from scratch.
Now we'll run the dev server:
Creating a new Vue 3 app
Straight off the bat, the way we bootstrap a new Vue app has changed. Rather than using new Vue()
, we now need to import the new createApp
method.
We then call this method, passing our Vue instance definition object, and assign the return object to a variable app
.
Next, we'll call the mount
method on app
and pass a CSS selector indicating our mount element, just like we did with the $mount
instance method in Vue 2.
src/main.js
Reason for change
With the old API, any global configuration we added (plugins, mixins, prototype properties etc) would permanently mutate global state. For example:
src/main.js
This really shows up as an issue in unit testing, as it makes it tricky to ensure that each test is isolated from the last.
Under the new API, calling createApp
returns a fresh app instance that will not be polluted by any global configuration applied to other instances.
Learn more: Global API change RFC.
Adding state properties
Our modal window can be in one of two states - opened, or closed. Let's manage this with a boolean state property modalOpen
which we'll give an initial value of false
.
Under Vue 2, we could do this by creating a data
property on our app instance and assigning an object to this where our modalOpen
property would be declared i.e.:
src/main.js
Slots Vue 3d
This is no longer allowed. Instead, data
must be assigned a factory function which returns the state object.
This is what you had to do for Vue components, but now it's enforced for Vue app instances as well.
src/main.js
Reason for change
The advantage of using an object for data rather than a factory function is that, firstly, it was syntactically simpler, and secondly, you could share top-level state between multiple root instances e.g.:
src/main.js
The use case for this is rare and can be worked around. Since having two types of declarations is not beginner-friendly, it was decided to remove this feature.
Learn more: Data object declaration removed RFC
Before we move on, let's also add a method to toggle the modalOpen
value. This is no different from Vue 2.
src/main.js
Using a root component
If you go to the browser now and check the console, you'll see the warning 'Component is missing render function', since we haven't yet defined a template for the root instance.
The best practice for Vue 2 is to create a minimal template for the root instance and create an App
component where the main app markup will be declared.
Let's do that here, as well.
Now we can get the root instance to render that component. The difference is that with Vue 2, we'd normally use a render function for doing this:
src/main.js
We can still do that, but Vue 3 has an even easier way - making App
a root component. To do this, we can remove the root instance definition and instead pass the App
component.
src/main.js
This means the App
component is not just rendered by the root instance but is the root instance.
While we're at it, let's simply the syntax a little by removing the app
variable:
src/main.js
Moving over to the root component now, let's re-add the state and method to this component:
src/App.vue
Let's also make a new component for the modal feature:
For now, we'll provide a minimal template including a slot for content. This ensures our modal is reusable. We'll add more to this component later.
src/Modal.vue
Multi-root templates
Let's now create the template for our root component. We'll create a button to open the modal which will trigger the toggleModalState
method.
We'll also use our just-created modal component which will be rendered conditional on the value of modalOpen
. Let's also insert a paragraph of text into the slot for content.
src/App.vue
Notice anything odd about this template? Look again. I'll wait.
That's right - there are two root elements. In Vue 3, thanks to a feature called fragments, it is no longer compulsory to have a single root element!
Never miss a new post!
Get our latest post in your inbox every Tuesday by subscribing to the Vue.js Developers Newsletter .
This subscription also includes Vue.js Developers promotional emails. You can opt-out at any time. View our privacy policy .
This form is protected by reCAPTCHA. The Google privacy policy and terms of service apply.
Refactoring with Composition API
The flagship feature of Vue 3 is the Composition API. This new API allows you to define component functionality using a setup
function rather than with properties you add to the component definition object.
Let's now refactor our App component to use the Composition API.
Before I explain the code, be clear that all we're doing is refactoring - the functionality of the component will be the same. Also note that the template is not changed as the Composition API only affects the way we define the component functionality, not the way we render it.
src/App.vue
setup
method
Firstly, notice we import the ref
function which allows us to define a reactive variable modalOpen
. This variable is equivalent to this.modalOpen
.
The toggleModalState
method is just a plain JavaScript function. However, notice that to change the value of modalOpen
in the method body, we need to change its sub-property value
. That's because reactive variables created using ref
are wrapped in an object. This is necessary to retain their reactivity as they're passed around.
It's best to consult the Vue Composition API docs if you want a detailed explanation of how refs
work.
Finally, we return modalOpen
and toggleModalState
from the setup
method, as these are the values that get passed to the template when it's rendered.
Reason for change
Keep in mind that the Composition API is not a change as it's purely optional to use. The main motivation is to allow for better code organization and the reuse of code between components (as mixins are essentially an anti-pattern).
You'd be correct in thinking that refactoring the App
component in this example to use the Composition API is unnecessary. But, if this were a much larger component, or we needed to share its features with other components, that's when you'd see its usefulness.
Providing a more in-depth example is beyond the scope of this blog post, so if you're interested to learn more about uses of the new API, check out my other article When To Use The New Vue Composition API (And When Not To).
Teleporting content
If you've ever created a modal feature before, you'll know that it's commonly positioned just before the closing </body>
tag.
This is done because modals usually have a page-covering background (see the image at the beginning if you don't know what I mean). To implement this with CSS, you don't want to have to deal with parent elements positioning and z-index stacking context, and so the simplest solution is to put the modal at the very bottom of the DOM.
This creates an issue with Vue.js, though, which assumes the UI will be built as a single tree of components. To allow segments of the tree to be moved elsewhere in the DOM, a new teleport
component has been added in Vue 3.
To use the teleport, let's first add an element to the page where we want our modal content to be moved to. We'll go to index.html
and place a div
with ID modal-wrapper
adjacent to Vue's mounting element.
index.html
Now, back in App.vue
, we're going to wrap the modal content in the teleport
component. We'll also need to specify a to
attribute which will be assigned a query selector identifying the target element, in this case, #modal-wrapper
.
src/App.vue
And that's it. Any content within the teleport
will be rendered within the target element. However, it will still function like it was in it's original position in the hierarchy (regarding props, events etc).
So after you've saved your code, reload the page, inspect the DOM in dev tools, and be amazed!
Learn more: Teleport RFC
Emitting an event
Let's now add a button to our modal allowing it to be closed. To do this, we're going to add a button
element to the modal tempate with a click handler that emits an event close
.
src/Modal.vue
This event is will then be captured by the parent component and will toggle the value of modalOpen
, logically making it false
and causing the window to close.
src/App.vue
So far, this feature is identical as it would be in Vue 2. However, in Vue 3 it's now recommended that you explicitly state a component's events using the new emits
component option. Just like with props, you can simply create an array of strings to name each event the component will emit.
src/Modal.vue
Reason for change
Imagine opening the file of a component that someone else wrote, and seeing its props and events explicitly declared. Immediately, you would understand the interface of this component i.e. what it's meant to send and receive.
In addition to providing self-documenting code, you can also use the events declaration to validate your event payload, though I couldn't find a reason to do that in this example.
Learn more: Emits Option RFC
Styling slot content
To make our modal reusable, we've provided a slot for content. Let's begin to style that content by adding a style
tag to the component.
It's a good practice to use scoped
CSS in our components to ensure the rules we provide don't have unintended effects on other content in the page.
Let's make it so any paragraph text that gets put into the slot is italic. To do this, we'll create a new CSS rule using the p
selector.
src/Modal.vue
If you try this, you'll see that it doesn't work. The issue is that scoped styling is determined at compile time when the slot content still belongs to the parent.
The solution provided by Vue 3 is to provide a pseudo selector ::v-slotted()
allowing you to target slot content with scoped rules in the component providing the slot.
Here's how we use it:
src/Modal.vue
Slots Vue Js
Vue 3 also includes some other new scoped styling selectors ::v-deep
and ::v-global
which you can learn more about here: Scoped Styles RFC
Other changes
Well, that's all the new features I could feasibly cover in a simple example. I got most of the main ones in, but here are some that I thought important enough to mention before concluding the article that you can research for yourself:
Added:
Removed:
- Event interface for components (no more event bus!)
Changed:
There are also various changes regarding Vue Router that you can check out here.
Props and $emit
When using Vue to develop a project, we divide the content of the project into modules, but sometimes there is data interaction between modules. In real project development, father-son and brother components need to pass values to each other. The most traditional way to pass values is props and $emit.
I. Props
Prop is some custom features that you can register on components. When a value is passed to a prop feature, it becomes an attribute of that component instance. To pass a title to the blog component, we can include it in the list of acceptable props for the component with a props option.
1. Examples of props passing values
Different parameters can also be passed to subcomponents:
2. Pros type validation
If we want each prop to have a specified value type and list prop as an object, the names and values of these properties are prop's respective names and types.
To make passing values more flexible, vue provides the following props authentication methods:
If a requirement is not met, Vue will warn you in the browser console. This is especially helpful in developing a component that will be used by others.
Note:
- All props make a single downlink binding between their father and son props: updates to the parent prop flow downward to the child components, but not vice versa. This prevents accidental changes in the state of the parent component from the child component, resulting in incomprehensible data flow to your application.
- Note that those props are validated before a component instance is created, so the properties of the instance (such as data, computed, etc.) are not available in default or validator functions.
- Because components cannot always predict the parameters that other components will pass in when they call the current component, vue also allows components to pass in some non-props features when they are called. These features are automatically added to the root element of the component.
2. $emit
A child component is called by a parent component. When a child component triggers a function, it needs a parent component response, using $emit.
Slots Vue 3.5
1. $emit simple example
We can also throw some data to the outside world when we throw an event.
If a component throws more than one data when throwing an event, it can continue to pass a value to $emit in the form of a parameter list, and then call it in the form of a parameter list in the parent component. But when there is a lot of data, it makes the code seem lengthy, so we recommend that all the data to be thrown be integrated into one object and passed in an object to the second parameter of the $emit function.
Slot Scope Vue
Slot slot slot
Slot is the second way that components are called, which improves the reusability of components.
When the child component calls the parent component and transfers its value, it tends to transfer in the data layer, but sometimes we hope that the child component can respond better to one of its parent components in the structure rendering layer, and the content of the child component can be rendered through the data control of the parent component.
If we need an operation result prompt box, the title, picture and button bar in the bullet box all render the concrete result according to the operation result. Using props or $emit can also achieve this effect, but if the results of multiple rendering are very different, then the code redundancy goes up again, so Vue provides a more concise way, that is, slots.
A slot is equivalent to encapsulating a component into a large framework. As for what and how to display it, it can transfer data when invoked, control it by data, and display the incoming content when invoked.
1. Small examples of slots
Note:
If the Alert component does not contain a < slot > element, anything between the start tag and the end tag will be discarded when the component is called.
2. Setting up backup slots for components
If a slot is reserved in a component, but the content is not distributed when the component is called, it may lead to some unpredictable errors in structure and logic, so sometimes it is necessary to set a default content for the slot.
3. Named sockets
To encapsulate a component with high reusability, a slot may not be enough. We can reserve multiple slots for each slot at different locations of the component structure, name each slot, and distribute the corresponding content to different slots when calling the component. This is the named slot.
Call the Alert component:
Abbreviation of Named Slot:
Note:
- There is no < slot > with the name attribute set, and the default name value is default.
- The v-slot attribute can only be added to one < template > (except for exclusive default slots).
4. Scope slots
Sometimes we need to access the data inside the component when we call it and distribute content to its socket. The data in the component can be bound to the slot prop, and then the data on the slot prop can be accessed at the same time when the component is called.
Call the slot and use the value of the slot prop:
Multiple slot prop s can be set for slots:
Receive:
Slots Vue 3.0
Slot prop can also be used for deconstruction:
Note:
If there are many slot props, it is recommended to integrate all data into one object and pass one slot prop.
Render function, JSX grammar and CreaeElement function
Render functions are the same as templates in creating html templates, but in some scenarios, the code implemented with templates is tedious and repetitive, so render functions can be used.
If render function is used in component rendering, then you can not use the < template > tag. Only < script > labels and < style > labels are required in component files.
Before we understand the render function, we need to define a concept of Vnode.
1. Vnode (virtual node)
When using Vue to develop a project, the structure rendered on the browser is rendered on the browser by Vue through various conditions, loops, calculations and other operations of the underlying mechanism. We regard the final result rendered on the browser as a real DOM tree.
But Vue responds to data efficiently. Faced with such efficient data response, it is also necessary to update nodes in the page equally efficiently. However, the structure of DOM is very large and complex, and it is particularly difficult to complete all DOM updates.
For example, we render a merchandise management list. When a certain value of a commodity changes, the list rendering of the page also needs to be updated. If we want to use native JS to render, we may need to re-render the entire table, or a row. If you want to locate a cell accurately, the code is very demanding.
Fortunately, when using Vue, we don't need to update the DOM tree manually using JS. Vue provides a virtual DOM tree through which it can track how it wants to change the real DOM.
DOM nodes written in vue file and DOM in render function are virtual DOM. When browser renders, all virtual DOM will be calculated and rendered on browser finally.
When we create a virtual DOM, we include all the information of the virtual DOM, such as child elements, class names, styles, locations, and so on.
Vue instances provide render functions to render virtual DOM, and render function parameters (also a function) to create virtual DOM.
2. Example render function
The < Home > component can be called normally, and the < div > rendered by the render function is taken when it is called.
render function:
- The parameter is the ce function (which is written as createElement in many places).
- The return value is a VNode (node to be rendered).
3. Parameter of render function - createElement function
The parameter of render function is the createElement function, which creates VNode as the return value of render.
The createElement function takes three parameters:
- Parametric 1:
String Object Function
An HTML tag name, component option object, or resolve has one of the async functions mentioned above. Required items. - Parametric 2:
Object
An object that contains all the relevant attributes of this tag (or template). Optional. - Parametric 3:
String Array
A child node or list created by createElement.
Following are detailed descriptions and examples of the second and third parameters of createElement:
4. JSX grammar
If the structure in a template is relatively simple, we can use the createElement function, but once the structure is slightly more complex, the code becomes particularly lengthy.
If you want to continue using render functions, you need to use JSX grammar. Because JSX grammar allows HTML structures to be written in JS code.
JSX grammar may be more widely used in React.
Added by flyersun on Thu, 01 Aug 2019 08:48:09 +0300