|
| 1 | +- Start Date: 2019-11-14 |
| 2 | +- Target Major Version: 3.x |
| 3 | +- Reference Issues: N/A |
| 4 | +- Implementation PR: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | + |
| 8 | +Remove support for the [inline-template feature](https://vuejs.org/v2/guide/components-edge-cases.html#Inline-Templates). |
| 9 | + |
| 10 | +# Motivation |
| 11 | + |
| 12 | +`inline-template` was originally included in Vue to address the cases where Vue is used to progressively enhance a traditionally server-rendered application (e.g. with Rails, Django or Laravel). It allows users to define the template of a child component directly inside a parent's template. |
| 13 | + |
| 14 | +The biggest issue with `inline-template` is that it makes template scoping very inconsistent. Without `inline-template`, a simple rule of thumb is that every variable appearing inside a template is either provided by the owner component, or by a directive that explicitly introduces scope variables (e.g. `v-for` and `v-slot`). `inline-template` breaks that assumption by mixing multiple scoping contexts in the same template: |
| 15 | + |
| 16 | +``` html |
| 17 | +<div> |
| 18 | + {{ parentMsg }} |
| 19 | + <child-comp inline-template> |
| 20 | + {{ parentMsg }} |
| 21 | + </child-comp> |
| 22 | +</div> |
| 23 | +``` |
| 24 | + |
| 25 | +In a standard component expecting slots, `{{ parentMsg }}` would work intuitively inside the slot content. However with `inline-template`, that is no longer the case. Similarly components with `v-for` + `inline-template` won't work as expected either: |
| 26 | + |
| 27 | +``` html |
| 28 | +<child-comp inline-template v-for="item in list"> |
| 29 | + {{ item.msg }} |
| 30 | +</child-comp> |
| 31 | +``` |
| 32 | + |
| 33 | +Here the inner template actually has no access to the iterated `item`. It's pointing to `this.item` on the child component instead. |
| 34 | + |
| 35 | +# Adoption strategy |
| 36 | + |
| 37 | +## Replacement 1: `<script>` tag |
| 38 | + |
| 39 | +Most of the use cases for `inline-template` assumes a no-build-tool setup, where all templates are written directly inside the HTML page. The most straightforward workaround in such cases is using `<script>` with an alternative type: |
| 40 | + |
| 41 | +``` html |
| 42 | +<script type="text/html" id="my-comp-template"> |
| 43 | + <div> |
| 44 | + {{ hello }} |
| 45 | + </div> |
| 46 | +</script> |
| 47 | +``` |
| 48 | + |
| 49 | +And in the component, target the template using a selector: |
| 50 | + |
| 51 | +``` js |
| 52 | +const MyComp = { |
| 53 | + template: '#my-comp-template', |
| 54 | + // ... |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +This doesn't require any build setup, works in all browsers, is not subject to in-DOM HTML parsing caveats (e.g. you can use camelCase prop names), and provides proper syntax highlighting in most IDEs. In a traditional server-side framework, these templates can be split out into server template partials (included into the main HTML template) for better maintainability. |
| 59 | + |
| 60 | +## Replacement 2: Default Slot |
| 61 | + |
| 62 | +A component previously using `inline-template` can be refactored into using the default slot - which makes the data scoping more explicit while preserving the convenience of writing child content inline: |
| 63 | + |
| 64 | +``` html |
| 65 | +<!-- before --> |
| 66 | +<my-comp inline-template :msg="parentMsg"> |
| 67 | + {{ msg }} {{ childState }} |
| 68 | +</my-comp> |
| 69 | + |
| 70 | +<!-- after --> |
| 71 | +<my-comp v-slot="{ childState }"> |
| 72 | + {{ parentMsg }} {{ childState }} |
| 73 | +</my-comp> |
| 74 | +``` |
| 75 | + |
| 76 | +The child, instead of providing no template, should now render the default slot (Note in v3 due to fragment support you can render a slot as the root now): |
| 77 | + |
| 78 | +``` html |
| 79 | +<!-- |
| 80 | + in child template, render default slot while passing |
| 81 | + in necessary private state of child. |
| 82 | +--> |
| 83 | +<slot :childState="childState" /> |
| 84 | +``` |
0 commit comments