Skip to content

Commit 99d18dc

Browse files
authored
Custom Elements Interop Improvements (#149)
* custom elements interop * add v-is for in-dom template workaround * code style tweaks * chore: more style consistency tweaks * Rename 0000-custom-elements-interop.md to 0027-custom-elements-interop.md
1 parent bb0a317 commit 99d18dc

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
- Start Date: 2020-03-25
2+
- Target Major Version: 3.x
3+
- Reference Issues: N/A
4+
5+
# Summary
6+
7+
- **Breaking:** Custom Elements whitelisting is now performed during template compilation, and should be configured via compiler options instead of runtime config.
8+
9+
- **Breaking:** Restrict the special `is` prop usage to the reserved `<component>` tag only.
10+
11+
- Introduce a new `v-is` directive to support 2.x use cases where `is` was used on native elements to work around native HTML parsing restrictions.
12+
13+
# Motivation
14+
15+
- Provide native custom element support in a more performant fashion
16+
- Improve support for [customized built-in elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example).
17+
18+
# Detailed design
19+
20+
## Autonomous Custom Elements
21+
22+
In Vue 2.x, whitelisting tags as custom elements is done via `Vue.config.ignoredElements`. The downside is that when this config option is used, a check needs to be performed on every vnode creation call.
23+
24+
**In Vue 3.0, this check is performed during template compilation.** For example, given the following template:
25+
26+
```html
27+
<plastic-button></plastic-button>
28+
```
29+
30+
The default generated render function code is (pseudo code):
31+
32+
```js
33+
function render() {
34+
const component_plastic_button = resolveComponent('plastic-button')
35+
return createVNode(component_plastic_button)
36+
}
37+
```
38+
39+
And it will emit a warning if no component named `plastic-button` was found.
40+
41+
If the user wishes to use a native custom element named `plastic-button`, the desired generated code should be:
42+
43+
```js
44+
function render() {
45+
return createVNode('plastic-button') // render as native element
46+
}
47+
```
48+
49+
To instruct the compiler to treat `<plastic-button>` as a custom element:
50+
51+
- If using a build step: pass the `isCustomElement` option to the Vue template compiler. If using `vue-loader`, this should be passed via `vue-loader`'s `compilerOptions` option:
52+
53+
```js
54+
// in webpack config
55+
rules: [
56+
{
57+
test: /\.vue$/,
58+
use: 'vue-loader',
59+
options: {
60+
compilerOptions: {
61+
isCustomElement: tag => tag === 'plastic-button'
62+
}
63+
}
64+
}
65+
// ...
66+
]
67+
```
68+
69+
- If using on-the-fly template compilation, pass it via `app.config`:
70+
71+
```js
72+
const app = Vue.createApp(/* ... */)
73+
74+
app.config.isCustomElement = tag => tag === 'plastic-button'
75+
```
76+
77+
Note the runtime config only affects runtime template compilation - it won't affect pre-compiled templates.
78+
79+
## Customized Built-in Elements
80+
81+
The Custom Elements specification provides a way to use custom elements as [Customized Built-in Element](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example) by adding the `is` attribute to a built-in element:
82+
83+
```html
84+
<button is="plastic-button">Click Me!</button>
85+
```
86+
87+
Vue's usage of the `is` special prop was simulating what the native attribute does before it was made universally available in browsers. However, in 2.x it is interpreted as rendering a Vue component with the name `plastic-button`. This blocks the native usage of Customized Built-in Element mentioned above.
88+
89+
In 3.0, we are limiting Vue's special treatment of the `is` prop to the `<component>` tag only.
90+
91+
- When used on the reserved `<component>` tag, it will behave exactly the same as in 2.x.
92+
93+
- When used on normal components, it will behave like a normal prop:
94+
95+
```html
96+
<foo is="bar" />
97+
```
98+
99+
- 2.x behavior: renders the `bar` component.
100+
- 3.x behavior: renders the `foo` component and passing the `is` prop.
101+
102+
- When used on plain elements, it will be passed to the `createElement` call as the `is` option, and also rendered as a native attribute. This supports the usage of customized built-in elements.
103+
104+
```html
105+
<button is="plastic-button">Click Me!</button>
106+
```
107+
108+
- 2.x behavior: renders the `plastic-button` component.
109+
- 3.x behavior: renders a native button by calling
110+
111+
```js
112+
document.createElement('button', { is: 'plastic-button' })
113+
```
114+
115+
## `v-is` for In-DOM Template Parsing Workarounds
116+
117+
> Note: this section only affects cases where Vue templates are directly written in the page's HTML.
118+
119+
When using in-DOM templates, the template is subject to native HTML parsing rules. Some HTML elements, such as `<ul>`, `<ol>`, `<table>` and `<select>` have restrictions on what elements can appear inside them, and some elements such as `<li>`, `<tr>`, and `<option>` can only appear inside certain other elements.
120+
121+
In 2.x we recommended working around with these restrictions by using the `is` prop on a native tag:
122+
123+
```html
124+
<table>
125+
<tr is="blog-post-row"></tr>
126+
</table>
127+
```
128+
129+
With the behavior change of `is` proposed above, we need to introduce a new directive `v-is` for working around these cases:
130+
131+
```html
132+
<table>
133+
<tr v-is="'blog-post-row'"></tr>
134+
</table>
135+
```
136+
137+
Note `v-is` functions like a dynamic 2.x `:is` binding - so to render a component by its registered name, its value should be a JavaScript string literal.
138+
139+
# Adoption strategy
140+
141+
- Compat build can detect use of `config.ignoredElements` and provide appropriate warning and guidance.
142+
143+
- A codemod can automatically convert all 2.x non `<component>` tags with `is` usage to `<component is>` (for SFC templates) or `v-is` (for in-DOM templates).

0 commit comments

Comments
 (0)