|
| 1 | +- Start Date: 2019-04-08 |
| 2 | +- Target Major Version: 3.x |
| 3 | +- Reference Issues: N/A |
| 4 | +- Implementation PR: N/A |
| 5 | + |
| 6 | +# Summary |
| 7 | + |
| 8 | +- Functional components must be written as plain functions |
| 9 | + - `{ functional: true }` option removed |
| 10 | + - `<template functional>` no longer supported |
| 11 | +- Async component must be created via the `createAsyncComponent` API method |
| 12 | + |
| 13 | +# Basic example |
| 14 | + |
| 15 | +``` js |
| 16 | +import { h } from 'vue' |
| 17 | + |
| 18 | +const FunctionalComp = props => { |
| 19 | + return h('div', `Hello! ${props.name}`) |
| 20 | +} |
| 21 | +``` |
| 22 | + |
| 23 | +``` js |
| 24 | +import { createAsyncComponent } from 'vue' |
| 25 | + |
| 26 | +const AsyncComp = createAsyncComponent(() => import('./Foo.vue')) |
| 27 | +``` |
| 28 | + |
| 29 | +# Motivation |
| 30 | + |
| 31 | +## Simplify Functional Components |
| 32 | + |
| 33 | +In 2.x, functional components must be created using the following format: |
| 34 | + |
| 35 | +``` js |
| 36 | +const FunctionalComp = { |
| 37 | + functional: true, |
| 38 | + render(h) { |
| 39 | + return h('div', `Hello! ${props.name}`) |
| 40 | + } |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +This has the following issues: |
| 45 | + |
| 46 | +- Even when the component needs nothing but the render function, it still needs to use the object with `functional: true`. |
| 47 | + |
| 48 | +- Some options are supported (e.g. `props` and `inject`) but others are not (e.g. `components`). However, users often expect all options to be supported because it looks so similar to a normal stateful component (especially when they use SFC with `<template functional>`). |
| 49 | + |
| 50 | +Another aspect of the problem is that we've noticed some users are using functional components solely for performance reasons, e.g. in SFCs with `<template functional>`, and are requesting us to support more stateful component options in functional components. However, I don't think this is something we should invest more time in. |
| 51 | + |
| 52 | +In v3, the performance difference between stateful and functional components has been drastically reduced and will be insignificant in most use cases. As a result there is no longer a strong incentive to use functional components just for performance, which also no longer justifies the maintenance cost of supporting `<template functional>`. Functional components in v3 should be used primarily for simplicity, not performance. |
| 53 | + |
| 54 | +# Detailed Design |
| 55 | + |
| 56 | +In 3.x, we intend to support functional components **only** as plain functions: |
| 57 | + |
| 58 | +``` js |
| 59 | +import { h } from 'vue' |
| 60 | + |
| 61 | +const FunctionalComp = (props, slots) => { |
| 62 | + return h('div', `Hello! ${props.name}`) |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +- The `functional` option is removed, and object format with `{ functional: true }` is no longer supported. |
| 67 | + |
| 68 | +- SFCs will no longer support `<template functional>` - if you need anything more than a function, just use a normal component. |
| 69 | + |
| 70 | +- The function signature has also changed - `h` is now imported globally. Instead of a render context, props and slots and other values are passed in. For more details on how the new arguments can replace 2.x functional render context, see the [Render Function API Change RFC](https://github.com/vuejs/rfcs/pull/28). |
| 71 | + |
| 72 | +## Runtime Props Validation |
| 73 | + |
| 74 | +Props declaration is now optional (only necessary when runtime validation is needed). To add runtime validation or default values, attach `props` to the function itself: |
| 75 | + |
| 76 | +``` js |
| 77 | +const FunctionalComp = props => { |
| 78 | + return h('div', `Hello! ${props.name}`) |
| 79 | +} |
| 80 | + |
| 81 | +FunctionalComp.props = { |
| 82 | + name: String |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +## Async Component Creation |
| 87 | + |
| 88 | +With the functional component change, Vue's runtime won't be able to tell whether a function is being provided as a functional component or an async component factory. So in v3 async components must now be created via a new API method: |
| 89 | + |
| 90 | +``` js |
| 91 | +import { createAsyncComponent } from 'vue' |
| 92 | + |
| 93 | +const AsyncComp = createAsyncComponent(() => import('./Foo.vue')) |
| 94 | +``` |
| 95 | + |
| 96 | +The method also supports advanced options: |
| 97 | + |
| 98 | +``` js |
| 99 | +const AsyncComp = createAsyncComponent({ |
| 100 | + factory: () => import('./Foo.vue'), |
| 101 | + delay: 200, |
| 102 | + timeout: 3000, |
| 103 | + error: ErrorComponent, |
| 104 | + loading: LoadingComponent |
| 105 | +}) |
| 106 | +``` |
| 107 | + |
| 108 | +This will make async component creation a little more verbose, but async component creation is typically a low-frequency use case, and are often grouped in the same file (the routing configuration). |
| 109 | + |
| 110 | +# Drawbacks |
| 111 | + |
| 112 | +- Migration cost |
| 113 | + |
| 114 | +# Alternatives |
| 115 | + |
| 116 | +N/A |
| 117 | + |
| 118 | +# Adoption strategy |
| 119 | + |
| 120 | +- For functional components, a compatibility mode can be provided for one-at-a-time migration. |
| 121 | + |
| 122 | +- For async components, the migration is straightforward and we can emit warnings when function components return Promise instead of VNodes. |
| 123 | + |
| 124 | +- SFCs using `<template functional>` should be converted to normal SFCs. |
0 commit comments