new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Vue · Guide · all subjects

components

198 notes in this subject, read out of this brain and free to use. This is page 2 of 4.

Prop validation with type requirements

Components can specify validation requirements for props. If a requirement is not met, Vue will warn in the browser's JavaScript console. Validation is specified by providing an object with validation requirements to defineProps() or the props option instead of an array of strings.

Basic prop type check

Basic type checking is done by specifying the constructor function as the prop value. For example: propA: Number. A null or undefined value allows any type.

Multiple prop types

To allow a prop to accept multiple types, use an array of constructor functions. For example: propB: [String, Number]

Required prop validation

To mark a prop as required, set required: true in the prop validation object. For example: propC: { type: String, required: true }. All props are optional by default unless required: true is specified.

Prop default value

A default value for a prop is specified with the default property. For example: propE: { type: Number, default: 100 }. The default value is used if the resolved prop value is undefined, including when the prop is absent or an explicit undefined value is passed.

Object and array default values must use factory functions

For object or array default values, a factory function must be used that returns the default value. The function receives the raw props as an argument. For example: propF: { type: Object, default(rawProps) { return { message: 'hello' } } }. This ensures each component instance gets its own copy of the default value.

Function default values are not factory functions

Unlike object or array defaults, function default values are not factory functions. The function is the actual default value to serve. For example: propH: { type: Function, default() { return 'Default function' } }

Custom validator function for props

A custom validator function can be specified to validate prop values. The validator receives the value as the first argument and the full props object as the second argument (in Vue 3.4+). It should return true if the value is valid. For example: propG: { validator(value, props) { return ['success', 'warning', 'danger'].includes(value) } }

Absent optional non-boolean prop value

An absent optional prop other than Boolean will have the value undefined.

Absent boolean prop defaults to false

An absent boolean prop will be cast to false. This can be changed by setting a default value explicitly, such as default: undefined to make it behave like a non-boolean prop.

defineProps code scope limitation

Code inside the defineProps() argument cannot access other variables declared in <script setup> because the entire expression is moved to an outer function scope when compiled.

Options API prop validation scope limitation

Props are validated before a component instance is created, so instance properties like data and computed will not be available inside default or validator functions.

Runtime type checks - native constructors

The type in prop validation can be one of the following native constructors: String, Number, Boolean, Array, Object, Date, Function, Symbol, Error.

Custom class type validation

The type can also be a custom class or constructor function, and the assertion is made with an instanceof check. For example, a prop can be validated to be an instance of a custom Person class.

Nullable type with array syntax

If a type is required but nullable, use the array syntax that includes null. Note that if type is just null without the array syntax, it will allow any type.

Boolean casting rules

Props with Boolean type have special casting rules to mimic the behavior of native boolean attributes. A prop declared with Boolean type without any value (e.g., <MyComponent disabled />) is equivalent to passing true, and omitting it is equivalent to passing false.

Boolean casting with multiple types - order matters

When a prop is declared to allow multiple types including Boolean and String, the boolean casting rule only applies if Boolean appears before String in the type array. For example, [Boolean, String] casts to true, but [String, Boolean] parses as an empty string (disabled='').

TypeScript prop declarations with script setup

With TypeScript in <script setup>, props can be declared using pure type annotations. For example: defineProps<{ title?: string; likes?: number }>(). This provides type safety at compile time.

Type-based props compilation to runtime declarations

When using Type-based props declarations with TypeScript, Vue will try to compile the type annotations into equivalent runtime prop declarations. For example, defineProps<{ msg: string }> is compiled into { msg: { type: String, required: true } }.

Provide/Inject solves prop drilling

Provide/Inject allows a parent component to serve as a dependency provider for all descendants, allowing any nested component to inject dependencies directly from ancestor components without passing props through intermediate components. This solves the prop drilling problem where props must be passed through every level of a component tree.

Composition API provide() function syntax

The provide() function from Vue accepts two arguments: an injection key (string or Symbol) and a provided value of any type. Example: provide('message', 'hello!'). Provide must be called synchronously inside setup() or directly in <script setup>. Reactive values like refs can be provided and will establish a reactive connection to descendant components.

Options API provide option syntax

In Options API, use the provide option to provide data to descendants. For static values, provide an object with key-value pairs: provide: { message: 'hello!' }. For per-instance state, use a function that returns an object, which allows access to this: provide() { return { message: this.message } }. Note that this approach does not make injections reactive.

App-level provide

Use app.provide(key, value) to provide data at the app level, making it available to all components rendered in the app. App-level provides are especially useful when writing plugins, as plugins typically cannot provide values using components.

Composition API inject() function syntax

The inject() function takes an injection key and optionally a default value. Example: const message = inject('message'). If the provided value is a ref, it is injected as-is and not automatically unwrapped, allowing the injector component to retain the reactivity connection to the provider. inject() should be called synchronously inside setup() or directly in <script setup>.

Composition API inject with default value

Pass a default value as the second argument to inject(): const value = inject('message', 'default value'). To avoid unnecessary computation or side effects, use a factory function for the default value by passing true as the third parameter: const value = inject('key', () => new ExpensiveClass(), true).

Options API inject option syntax

Use the inject option with an array for simple cases: inject: ['message']. Injections are resolved before the component's own state, so injected properties can be accessed in data(). For aliasing or default values, use object syntax: inject: { message: { from: 'message', default: 'default value' } }. For non-primitive values, use a factory function: inject: { user: { default: () => ({ name: 'John' }) } }.

Injection aliasing in Options API

Use the object syntax in inject to expose an injected property under a different local key: inject: { localMessage: { from: 'message' } }. This injects the value provided with key 'message' and exposes it as this.localMessage.

Multiple parents with same injection key resolution

When multiple parents provide data with the same injection key, inject() resolves to the value from the closest parent in the component's parent chain.

Injection default values in Composition API

By default, inject assumes the injection key is provided somewhere in the parent chain and will produce a runtime warning if not found. Declare a default value to make an injected property optional: const value = inject('message', 'default value').

Best practice for reactive provide/inject mutations

Keep any mutations to reactive state inside the provider component whenever possible. This co-locates the provided state and its mutations in the same component, making it easier to maintain. If an injector component needs to update data, the provider should expose a mutation function.

Provide mutation function example

When an injector component needs to update provided state, the provider can expose an object containing both the state and a function to mutate it. Example: provide('location', { location, updateLocation }) where updateLocation is a function that mutates the location ref.

Readonly wrapper for provided values

Use readonly() to wrap provided reactive values if you want to ensure the data cannot be mutated by injector components. Example: provide('read-only-count', readonly(count)).

Options API reactive provide/inject with computed

To make injections reactively linked to the provider in Options API, provide a computed property: provide() { return { message: computed(() => this.message) } }. This establishes a reactive connection between provider and injector components.

Symbol injection keys for large applications

In large applications or when authoring components for other developers, use Symbol injection keys instead of strings to avoid potential key collisions. Export Symbols in a dedicated file and import them in both provider and injector components.

Symbol injection key example

Create a keys.js file exporting Symbols: export const myInjectionKey = Symbol(). In the provider, import and use it: provide(myInjectionKey, { /* data */ }). In the injector, import and use it: const injected = inject(myInjectionKey).

Local registration with script setup in SFCs

When using `<script setup>` in Single File Components, imported components can be locally used without explicit registration. The component is automatically available in the template when imported.

Global component registration with .component() method

Use the `.component()` method on a Vue application instance to register components globally. The method takes two arguments: the registered name (a string) and the implementation (the component object). Global registration makes components available in templates of any component within that application, including all subcomponents.

Global registration example with imported SFC

When registering a Single File Component globally, import the .vue file and pass it to app.component(). Example: `import MyComponent from './App.vue'; app.component('MyComponent', MyComponent)`.

Chaining .component() calls for multiple global registrations

The `.component()` method can be chained to register multiple components in sequence. Example: `app.component('ComponentA', ComponentA).component('ComponentB', ComponentB).component('ComponentC', ComponentC)`.

Global registration makes dependency relationships less explicit

Global registration makes it difficult to locate a child component's implementation from a parent component using it in large applications. This reduces explicitness of dependencies and can negatively affect long-term maintainability, similar to overusing global variables.

Local registration example with script setup

Example of local registration with `<script setup>`: import the component in the script section and use it directly in the template without registering it. The component is automatically available.

Local registration using components option

In non-`<script setup>` components, use the `components` option to locally register components. The `components` object has component names as keys and their implementations as values. Locally registered components are only available to the current component, not to descendant components.

Local registration example with components option

Example of local registration using the components option: `export default { components: { ComponentA }, setup() { /* ... */ } }`. The component name is the key and the imported component is the value.

Locally registered components not available in descendants

Locally registered components are only available to the current component where they are registered. They are not automatically available in child or descendant components.

PascalCase recommended for component naming

Use PascalCase when registering components because PascalCase names are valid JavaScript identifiers (making imports and registration easier and improving IDE auto-completion), and `<PascalCase />` clearly indicates a Vue component rather than native HTML in templates, differentiating Vue components from custom elements.

Vue supports kebab-case tag resolution for PascalCase components

Vue resolves kebab-case tags to components registered using PascalCase. A component registered as `MyComponent` can be referenced in templates as both `<MyComponent>` and `<my-component>`, allowing the same JavaScript registration code to work regardless of template source.

PascalCase tags not usable in in-DOM templates

PascalCase component tags cannot be used directly in in-DOM templates due to HTML parsing caveats. Use kebab-case tags instead (e.g., `<my-component>` instead of `<MyComponent>`) when working with in-DOM templates.

defineModel() under the hood implementation

defineModel() is a convenience macro that the compiler expands to a prop named modelValue and an event named update:modelValue. The parent component's v-model="foo" is compiled to :modelValue="foo" @update:modelValue="$event => (foo = $event)" on the child component.

defineModel() macro for v-model binding

Starting in Vue 3.4, the recommended approach to implement two-way binding on a component is using the defineModel() macro in the Composition API. The macro returns a ref whose .value is synced with the value bound by the parent v-model. When the child mutates the ref, it causes the parent bound value to be updated. The value returned by defineModel() can be accessed and mutated like any other ref.

defineModel() with prop options

Prop options can be passed to defineModel() by passing an options object. For example: const model = defineModel({ required: true }) makes the v-model required, or const model = defineModel({ default: 0 }) provides a default value.

defineModel() default value desynchronization pitfall

If defineModel() has a default value and the parent component does not provide a value for the prop, it can cause desynchronization between parent and child. The parent value will be undefined while the child value will be the default. Additionally, default values for mutable reference types like arrays or objects should be wrapped in functions to avoid accidental modification and external side effects when using withDefaults.

Component v-model implementation via props and emits

In Options API prior to Vue 3.4, v-model on a component can be implemented by declaring a modelValue prop and emitting an update:modelValue event. The child component binds the value attribute of a native input element to the modelValue prop and emits update:modelValue with the new value when an input event is triggered.

Component v-model using writable computed property

In Options API, v-model can be implemented using a writable computed property with a getter and setter. The getter returns the modelValue prop and the setter emits the update:modelValue event. The computed property can then be bound to a native input with v-model.

v-model argument binding on components

v-model on a component can accept an argument to support multiple bindings. For example, v-model:title="bookTitle" instead of the default v-model. In the Composition API with Vue 3.4+, this is supported by passing a string to defineModel() as its first argument: const title = defineModel('title').

Multiple v-model bindings on single component

Multiple v-model bindings can be created on a single component instance by using different arguments. For example, v-model:first-name="first" v-model:last-name="last". Each v-model will sync to a different prop without needing extra options in the component.

v-model custom modifiers in Composition API

In Composition API with Vue 3.4+, modifiers added to a component v-model can be accessed by destructuring the defineModel() return value: const [model, modifiers] = defineModel(). The modifiers object contains the applied modifiers as properties. To conditionally adjust how the value should be read or written based on modifiers, pass get and set options to defineModel() that receive and return transformed values.

v-model custom modifiers in Options API

In Options API, modifiers added to a component v-model are provided via a modelModifiers prop that defaults to an empty object. The component can check the modelModifiers object keys and write a handler to change the emitted value based on the modifiers. For example, if v-model.capitalize="myText" is used, this.modelModifiers will contain { capitalize: true }.

v-model modifiers with arguments naming convention

For v-model bindings with both an argument and modifiers, the generated prop name for modifiers will be arg + 'Modifiers'. For example, v-model:title.capitalize="myText" results in a titleModifiers prop containing { capitalize: true }.

Multiple v-model with different modifiers

Multiple v-model bindings with different arguments and modifiers can be used on the same component. For example: v-model:first-name.capitalize="first" v-model:last-name.uppercase="last". In Composition API with Vue 3.4+, destructure each with defineModel: const [firstName, firstNameModifiers] = defineModel('firstName') and const [lastName, lastNameModifiers] = defineModel('lastName').

v-model wrapping native input elements

A component that wraps native input elements with v-model can bind the defineModel() ref directly to the native input using v-model. This makes it straightforward to wrap native input elements while providing the same v-model usage: const model = defineModel() then <input v-model="model" />.

Give your agent this brain