You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: expand the page on render functions, especially their use of slots (#712)
* docs: expand the page on render functions, especially their use of slots
* Update src/guide/render-function.md
Co-authored-by: Ben Hong <[email protected]>
* Update src/guide/render-function.md
Co-authored-by: Ben Hong <[email protected]>
* docs: add a note about global registration and using resolveComponent
Co-authored-by: Ben Hong <[email protected]>
If there are no props then the children can usually be passed as the second argument. In cases where that would be ambiguous, `null` can be passed as the second argument to keep the children as the third argument.
166
+
167
167
## Complete Example
168
168
169
169
With this knowledge, we can now finish the component we started:
@@ -240,6 +240,47 @@ render() {
240
240
}
241
241
```
242
242
243
+
## Creating Component VNodes
244
+
245
+
To create a VNode for a component, the first argument passed to `h` should be the component itself:
246
+
247
+
```js
248
+
render() {
249
+
returnVue.h(ButtonCounter)
250
+
}
251
+
```
252
+
253
+
If we need to resolve a component by name then we can call `resolveComponent`:
`resolveComponent` is the same function that templates use internally to resolve components by name.
263
+
264
+
A `render` function will normally only need to use `resolveComponent` for components that are [registered globally](/guide/component-registration.html#global-registration). [Local component registration](/guide/component-registration.html#local-registration) can usually be skipped altogether. Consider the following example:
Rather than registering a component by name and then looking it up we can use it directly instead:
277
+
278
+
```js
279
+
render() {
280
+
returnVue.h(ButtonCounter)
281
+
}
282
+
```
283
+
243
284
## Replacing Template Features with Plain JavaScript
244
285
245
286
### `v-if` and `v-for`
@@ -268,6 +309,8 @@ render() {
268
309
}
269
310
```
270
311
312
+
In a template it can be useful to use a `<template>` tag to hold a `v-if` or `v-for` directive. When migrating to a `render` function, the `<template>` tag is no longer required and can be discarded.
313
+
271
314
### `v-model`
272
315
273
316
The `v-model` directive is expanded to `modelValue` and `onUpdate:modelValue` props during template compilation—we will have to provide these props ourselves:
@@ -297,7 +340,7 @@ render() {
297
340
298
341
#### Event Modifiers
299
342
300
-
For the `.passive`, `.capture`, and `.once` event modifiers, they can be concatenated after event name using camel case.
343
+
For the `.passive`, `.capture`, and `.once` event modifiers, they can be concatenated after the event name using camel case.
You can access slot contents as Arrays of VNodes from [`this.$slots`](../api/instance-properties.html#slots):
392
+
We can access slot contents as Arrays of VNodes from [`this.$slots`](../api/instance-properties.html#slots):
350
393
351
394
```js
352
395
render() {
353
396
// `<div><slot></slot></div>`
354
-
returnVue.h('div', {}, this.$slots.default())
397
+
returnVue.h('div', this.$slots.default())
355
398
}
356
399
```
357
400
358
401
```js
359
402
props: ['message'],
360
403
render() {
361
404
// `<div><slot :text="message"></slot></div>`
362
-
returnVue.h('div', {}, this.$slots.default({
405
+
returnVue.h('div', this.$slots.default({
363
406
text:this.message
364
407
}))
365
408
}
366
409
```
367
410
368
-
To pass slots to a child component using render functions:
411
+
For component VNodes, we need to pass the children to `h` as an Object rather than an Array. Each property is used to populate the slot of the same name:
// in the form of { name: props => VNode | Array<VNode> }
379
422
{
@@ -384,6 +427,95 @@ render() {
384
427
}
385
428
```
386
429
430
+
The slots are passed as functions, allowing the child component to control the creation of each slot's contents. Any reactive data should be accessed within the slot function to ensure that it's registered as a dependency of the child component and not the parent. Conversely, calls to `resolveComponent` should be made outside the slot function, otherwise they'll resolve relative to the wrong component:
431
+
432
+
```js
433
+
// `<MyButton><MyIcon :name="icon" />{{ text }}</MyButton>`
434
+
render() {
435
+
// Calls to resolveComponent should be outside the slot function
436
+
constButton=Vue.resolveComponent('MyButton')
437
+
constIcon=Vue.resolveComponent('MyIcon')
438
+
439
+
returnVue.h(
440
+
Button,
441
+
null,
442
+
{
443
+
// Use an arrow function to preserve the `this` value
444
+
default: (props) => {
445
+
// Reactive properties should be read inside the slot function
446
+
// so that they become dependencies of the child's rendering
447
+
return [
448
+
Vue.h(Icon, { name:this.icon }),
449
+
this.text
450
+
]
451
+
}
452
+
}
453
+
)
454
+
}
455
+
```
456
+
457
+
If a component receives slots from its parent, they can be passed on directly to a child component:
458
+
459
+
```js
460
+
render() {
461
+
returnVue.h(Panel, null, this.$slots)
462
+
}
463
+
```
464
+
465
+
They can also be passed individually or wrapped as appropriate:
Behind the scenes, templates use `resolveDynamicComponent` to implement the `is` attribute. We can use the same function if we need all the flexibility provided by `is` in our `render` function:
Similarly, if the value passed to `is` is a component options object then there's no need to resolve anything, it can be passed directly as the first argument of `h`.
516
+
517
+
Much like a `<template>` tag, a `<component>` tag is only required in templates as a syntactical placeholder and should be discarded when migrating to a `render` function.
518
+
387
519
## JSX
388
520
389
521
If we're writing a lot of `render` functions, it might feel painful to write something like this:
0 commit comments