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
Corrected and improved docs on type definitions for Custom Elements. (#3073)
Corrected and improved documentation on type definitions for Custom Elements.
The doc was incorrect for two reasons:
- For Vue-based custom elements, the types passed into `GlobalComponents` need to be the Vue component types, not the custom element types, or the custom elements will not be type checked in Vue templates.
- The name of the elements need to have at least two words and a hyphen, otherwise with single words the custom elements will not work at all.
Copy file name to clipboardExpand all lines: src/guide/extras/web-components.md
+259-11
Original file line number
Diff line number
Diff line change
@@ -220,6 +220,8 @@ If the custom elements will be used in an application that is also using Vue, yo
220
220
It is recommended to export the individual element constructors to give your users the flexibility to import them on-demand and register them with desired tag names. You can also export a convenience function to automatically register all elements. Here's an example entry point of a Vue custom element library:
221
221
222
222
```js
223
+
// elements.js
224
+
223
225
import { defineCustomElement } from'vue'
224
226
importFoofrom'./MyFoo.ce.vue'
225
227
importBarfrom'./MyBar.ce.vue'
@@ -236,31 +238,277 @@ export function register() {
236
238
}
237
239
```
238
240
239
-
If you have many components, you can also leverage build tool features such as Vite's [glob import](https://vitejs.dev/guide/features.html#glob-import) or webpack's [`require.context`](https://webpack.js.org/guides/dependency-management/#requirecontext) to load all components from a directory.
241
+
A consumer can use the elements in a Vue file,
242
+
243
+
```vue
244
+
<script setup>
245
+
import { register } from 'path/to/elements.js'
246
+
register()
247
+
</script>
248
+
249
+
<template>
250
+
<my-foo ...>
251
+
<my-bar ...></my-bar>
252
+
</my-foo>
253
+
</template>
254
+
```
255
+
256
+
or in any other framework such as one with JSX, and with custom names:
240
257
241
-
### Web Components and TypeScript {#web-components-and-typescript}
258
+
```jsx
259
+
import { MyFoo, MyBar } from'path/to/elements.js'
260
+
261
+
customElements.define('some-foo', MyFoo)
262
+
customElements.define('some-bar', MyBar)
263
+
264
+
exportfunctionMyComponent() {
265
+
return<>
266
+
<some-foo ...>
267
+
<some-bar ...></some-bar>
268
+
</some-foo>
269
+
</>
270
+
}
271
+
```
242
272
243
-
If you are developing an application or a library, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements.
273
+
### Vue-based Web Components and TypeScript {#web-components-and-typescript}
244
274
245
-
Custom elements are registered globally using native APIs, so by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings using the the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) in Vue templates and/or in [JSX](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements):
275
+
When writing Vue SFC templates, you may want to [type check](/guide/scaling-up/tooling.html#typescript) your Vue components, including those that are defined as custom elements.
276
+
277
+
Custom elements are registered globally in browsers using their built-in APIs, and by default they won't have type inference when used in Vue templates. To provide type support for Vue components registered as custom elements, we can register global component typings by augmenting the [`GlobalComponents` interface](https://github.com/vuejs/language-tools/blob/master/packages/vscode-vue/README.md#usage) for type checking in Vue templates (JSX users can augment the [JSX.IntrinsicElements](https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements) type instead, which is not shown here).
278
+
279
+
Here is how to define the type for a custom element made with Vue:
Suppose that `some-lib` builds its source TypeScript files into a `dist/` folder. A user of
425
+
`some-lib` can then import `SomeElement` and use it in a Vue SFC like so:
255
426
256
-
// register global typings
427
+
```vue
428
+
<script setup lang="ts">
429
+
// This will create and register the element with the browser.
430
+
import 'some-lib/dist/SomeElement.js'
431
+
432
+
// A user that is using TypeScript and Vue should additionally import the
433
+
// Vue-specific type definition (users of other frameworks may import other
434
+
// framework-specific type definitions).
435
+
import type {} from 'some-lib/dist/SomeElement.vue.js'
436
+
437
+
import { useTemplateRef, onMounted } from 'vue'
438
+
439
+
const el = useTemplateRef('el')
440
+
441
+
onMounted(() => {
442
+
console.log(
443
+
el.value!.foo,
444
+
el.value!.bar,
445
+
el.value!.lorem,
446
+
el.value!.someMethod()
447
+
)
448
+
449
+
// Do not use these props, they are `undefined` (IDE will show them crossed out):
450
+
el.$props
451
+
el.$emit
452
+
})
453
+
</script>
454
+
455
+
<template>
456
+
<!-- Now we can use the element, with type checking: -->
457
+
<some-element
458
+
ref="el"
459
+
:foo="456"
460
+
:blah="'hello'"
461
+
@apple-fell="
462
+
(event) => {
463
+
// The type of `event` is inferred here to be `AppleFellEvent`
464
+
}
465
+
"
466
+
></some-element>
467
+
</template>
468
+
```
469
+
470
+
If an element does not have type definitions, the types of the properties and events can be
471
+
defined in a more manual fashion:
472
+
473
+
```vue
474
+
<script setup lang="ts">
475
+
// Suppose that `some-lib` is plain JS without type defintions, and TypeScript
476
+
// cannot infer the types:
477
+
import { SomeElement } from 'some-lib'
478
+
479
+
// We'll use the same type helper as before.
480
+
import { DefineCustomElement } from './DefineCustomElement'
481
+
482
+
type SomeElementProps = { foo?: number; bar?: string }
483
+
type SomeElementEvents = { 'apple-fell': AppleFellEvent }
484
+
interface AppleFellEvent extends Event {
485
+
/* ... */
486
+
}
487
+
488
+
// Add the new element type to Vue's GlobalComponents type.
257
489
declare module 'vue' {
258
-
exportinterfaceGlobalComponents {
259
-
Counter:typeofCounter
490
+
interface GlobalComponents {
491
+
'some-element': DefineCustomElement<
492
+
SomeElementProps,
493
+
SomeElementEvents
494
+
>
260
495
}
261
496
}
497
+
498
+
// ... same as before, use a reference to the element ...
499
+
</script>
500
+
501
+
<template>
502
+
<!-- ... same as before, use the element in the template ... -->
503
+
</template>
262
504
```
263
505
506
+
Custom Element authors should not automatically export framework-specific custom
507
+
element type definitions from their libraries, for example they should not
508
+
export them from an `index.ts` file that also exports the rest of the library,
509
+
otherwise users will have unexpected module augmentation errors. Users should
510
+
import the framework-specific type definition file that they need.
511
+
264
512
## Web Components vs. Vue Components {#web-components-vs-vue-components}
265
513
266
514
Some developers believe that framework-proprietary component models should be avoided, and that exclusively using Custom Elements makes an application "future-proof". Here we will try to explain why we believe that this is an overly simplistic take on the problem.
0 commit comments