Vue Slots Jsx
TLDR; I show that Vue can use JSX to utilize the React pattern of a render prop. Source code here
渲染函数 & JSX — Vue.jsrender函数特点render函数和模板一样,模板可以做的事情它都可以做render函数是最接近编译器的函数render函数返回vnode模板和jsx会先编译成render函数然后在返回vnode组件树中的所有 V. Stack Overflow for Teams is a private, secure spot for you and your coworkers to find and share information. JSX isn't natively supported by the browser, a module bundler like Webpack will transpile (convert from JSX to Javascript) from JSX, which isn't understood by the browser, to a format that is understood by the browser. That's going to be the first step you need to take and probably what you're missing.
Having both children and slots therefore allows you to choose whether this component knows about a slot system or perhaps delegates that responsibility to another component by passing along children. Template Compilation. You may be interested to know that Vue’s templates actually compile to render functions.
In Vue, templates are typically how we compose/extend/mix/open source our components. This is contra the experience of React developers who write most of their compiled html in JSX.
Thanks to a similar architecture of using the virtualDOM + createElement API and babel-plugin-transform-vue-js, we can write our Vue components in almost the same way we write React! (Not that we should do this for all components, but it's fun to see design patterns and utilize them).
UPDATE: I'm using render props in https://github.com/educents/vue-autosuggest in the renderSuggestion prop, so go check it out!
Render props demo
For demonstration, I will use an example from Use a Render Prop! article by Michael Jackson.
First the SFC:
Here in our parent App.vue
, the Mouse
component will be our child component. Inside Mouse.js
we will call our renderProp
function callback inside the render method. I've mixed JSX inside the SFC's methods
section as you can't use jsx inside template
. Here's our Mouse
component:
Yes this is a Vue component, not React. Compared with the React version:
Some differences between the two:
- Vue has built in prop type validation.
- You can't inline an anonymous function that returns jsx inside a template. I've named the callback __render(A single _ before render also throws a Vue warning). You can reasonably use a simple Vue .js component as the parent to pass in an anonymous function, but alas, we're Vue developers so we can mix our templates with our JSX and be happy about it!
- We're passing back
this
(the Vue instance) instead of the React state, but utilize destructuring all the same to pass backx
andy
. - The obvious Vue differences such as components are just objects, not javascript classes, there is no 'setState' as it converts it's Reactive data properties (the corollary to React's state) to getter/setters using Object.defineProperty.
onMouseMove
vsonMousemove
💣
Vue Render Prop
So there you go, a fairly similar and transferable component design.
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.
Scoped Slots
In case you are wondering what's the equivalent pattern in Vue, it's called scoped slots (and if using JSX it works the same as React)
— Evan You (@youyuxi) September 25, 2017Vue creator Evan You on render props.
If you were to do 'render props' with templates, a similar design would be to use scoped slots and would look something like this:
Vue Jsx Slot Props
Vue Scoped slots are powerful when using templates.
Some advantages to scoped slots are that:
Vue Slot Jsx
- Custom parent-child template injection without a render function or jsx.
- You can specify default content easily. In the above example, I pass in a specified slot, that defines a custom message, but when I don't specify a slot, it will fallback to the default slot. A default slot also gives users of the component a 'component api' so that you don't have to guess what you might need to render.
- Uses destructuring similar to jsx render callback
- Parent content to be rendered with child data is 'inline' with the template
- You will probably never be able to inline a jsx function in your template (https://github.com/vuejs/vue/issues/7439)
Closing Remarks
Why would I want to use the render prop pattern or JSX? The nice thing about this is that using the render function + JSX is a closer-to-the-compiler alternative and allows fine-grain control over rendering. You won't be able to use custom directives like v-model or v-if. Render props themselves have a lot of benefits, as outlined in this episode of Full stack radio where Adam interviews Kent C. Dodds.
If you're a Vue user, does this type of component composing surprise you? If so, I recommend going and reading the official docs which actually explain JSX and render functions in greater detail.
I love that we can share design principles between frameworks. It gives me warm fuzzy feelings in a cruel, cold world that believes there is a war on frameworks. In 2018, try and find the common ground.
If you enjoyed reading this, follow me on twitter where my DM's are always open!
Further reading:
- Source code: https://github.com/darrenjennings/vue-jsx-render-props-example
- Official Vue documentation: https://vuejs.org/v2/guide/render-function.html#Basics
- Original article on using render props and what this article's title is referencing: https://cdb.Reacttraining.com/use-a-render-prop-50de598f11ce
- A helpful article on similarities in frameworks converging React + Vue + Angular: http://varun.ca/convergence/
This page assumes you’ve already read the Components Basics. Read that first if you are new to components.
Slot Content
Vue implements a content distribution API that’s modeled after the current Web Components spec draft, using the <slot>
element to serve as distribution outlets for content.
This allows you to compose components like this:
Then in the template for <navigation-link>
, you might have:
When the component renders, the <slot>
element will be replaced by “Your Profile”. Slots can contain any template code, including HTML:
Or even other components:
If <navigation-link>
did not contain a <slot>
element, any content passed to it would simply be discarded.
Jsx Vue Slots
Named Slots
There are times when it’s useful to have multiple slots. For example, in a hypothetical base-layout
component with the following template:
For these cases, the <slot>
element has a special attribute, name
, which can be used to define additional slots:
To provide content to named slots, we can use the slot
attribute on a <template>
element in the parent:
Or, the slot
attribute can also be used directly on a normal element:
There can still be one unnamed slot, which is the default slot that serves as a catch-all outlet for any unmatched content. In both examples above, the rendered HTML would be:
Default Slot Content
There are cases when it’s useful to provide a slot with default content. For example, a <submit-button>
component might want the content of the button to be “Submit” by default, but also allow users to override with “Save”, “Upload”, or anything else.
To achieve this, specify the default content in between the <slot>
tags.
If the slot is provided content by the parent, it will replace the default content.
Compilation Scope
When you want to use data inside a slot, such as in:
That slot has access to the same instance properties (i.e. the same “scope”) as the rest of the template. The slot does not have access to <navigation-link>
‘s scope. For example, trying to access url
would not work. As a rule, remember that:
Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope.
Scoped Slots
New in 2.1.0+
Sometimes you’ll want to provide a component with a reusable slot that can access data from the child component. For example, a simple <todo-list>
component may contain the following in its template:
But in some parts of our app, we want the individual todo items to render something different than just the todo.text
. This is where scoped slots come in.
To make the feature possible, all we have to do is wrap the todo item content in a <slot>
element, then pass the slot any data relevant to its context: in this case, the todo
object:
Now when we use the <todo-list>
component, we can optionally define an alternative <template>
for todo items, but with access to data from the child via the slot-scope
attribute:
In 2.5.0+, slot-scope
is no longer limited to the <template>
element, but can instead be used on any element or component in the slot.
Destructuring slot-scope
The value of slot-scope
can actually accept any valid JavaScript expression that can appear in the argument position of a function definition. This means in supported environments (single-file components or modern browsers) you can also use ES2015 destructuring in the expression, like so:
This is a great way to make scoped slots a little cleaner.