Skip to content

Remove inline-template #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 13, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions active-rfcs/0016-remove-inline-templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
- Start Date: 2019-11-14
- Target Major Version: 3.x
- Reference Issues: N/A
- Implementation PR: (leave this empty)

# Summary

Remove support for the [inline-template feature](https://vuejs.org/v2/guide/components-edge-cases.html#Inline-Templates).

# Motivation

`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.

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:

``` html
<div>
{{ parentMsg }}
<child-comp inline-template>
{{ parentMsg }}
</child-comp>
</div>
```

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:

``` html
<child-comp inline-template v-for="item in list">
{{ item.msg }}
</child-comp>
```

Here the inner template actually has no access to the iterated `item`. It's pointing to `this.item` on the child component instead.

# Adoption strategy

## Replacement 1: `<script>` tag

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:

``` html
<script type="text/html" id="my-comp-template">
<div>
{{ hello }}
</div>
</script>
```

And in the component, target the template using a selector:

``` js
const MyComp = {
template: '#my-comp-template',
// ...
}
```

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.

## Replacement 2: Default Slot

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:

``` html
<!-- before -->
<my-comp inline-template :msg="parentMsg">
{{ msg }} {{ childState }}
</my-comp>

<!-- after -->
<my-comp v-slot="{ childState }">
{{ parentMsg }} {{ childState }}
</my-comp>
```

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):

``` html
<!--
in child template, render default slot while passing
in necessary private state of child.
-->
<slot :childState="childState" />
```