Skip to content

[next] tweaks and typos - components & reusability #1432

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 1 commit into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions src/guide/components/async.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Basic Usage

In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it's needed. To make that possible, Vue has a [`defineAsyncComponent`](/api/general.html#defineasynccomponent) method:
In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it's needed. To make that possible, Vue has a [`defineAsyncComponent`](/api/general.html#defineasynccomponent) function:

```js
import { defineAsyncComponent } from 'vue'
Expand All @@ -16,7 +16,7 @@ const AsyncComp = defineAsyncComponent(() => {
// ... use `AsyncComp` like a normal component
```

As you can see, this method accepts a loader function that returns a Promise. The Promise's `resolve` callback should be called when you have retrieved your component definition from the server. You can also call `reject(reason)` to indicate the load has failed.
As you can see, `defineAsyncComponent` accepts a loader function that returns a Promise. The Promise's `resolve` callback should be called when you have retrieved your component definition from the server. You can also call `reject(reason)` to indicate the load has failed.

[ES module dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports) also returns a Promise, so most of the time we will use it in combination with `defineAsyncComponent`. Bundlers like Vite and webpack also support the syntax, so we can use it to import Vue SFCs:

Expand Down
10 changes: 5 additions & 5 deletions src/guide/components/attrs.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Then the final rendered DOM would now become:

### `v-on` Listener Inheritance

Same rule applies to `v-on` event listeners:
The same rule applies to `v-on` event listeners:

```vue-html
<MyButton @click="onClick" />
Expand Down Expand Up @@ -94,15 +94,15 @@ export default {

</div>

The common scenario for disabling an attribute inheritance is when attributes need to be applied to other elements besides the root node. By setting the `inheritAttrs` option to `false`, you can take full control over where the fallthrough attributes should be applied to.
The common scenario for disabling attribute inheritance is when attributes need to be applied to other elements besides the root node. By setting the `inheritAttrs` option to `false`, you can take full control over where the fallthrough attributes should be applied.

These fallthrough attributes can be accessed directly in template expressions as `$attrs`:

```vue-html
<span>Fallthrough attributes: {{ $attrs }}</span>
```

The `$attrs` object includes all attributes not included to component `props` and `emits` properties (e.g., `class`, `style`, `v-on` listeners, etc.).
The `$attrs` object includes all attributes that are not declared by the component's `props` or `emits` options (e.g., `class`, `style`, `v-on` listeners, etc.).

Using our `<MyButton>` component example from the [previous section](#attribute-inheritance) - sometimes we may need to wrap the actual `<button>` element with an extra `<div>` for styling purposes:

Expand All @@ -120,11 +120,11 @@ We want all fallthrough attributes like `class` and `v-on` listeners to be appli
</div>
```

Remember that [`v-bind` without argument](/guide/essentials/template-syntax.html#dynamically-binding-multiple-attributes) binds every property of an object as attributes to the target element.
Remember that [`v-bind` without an argument](/guide/essentials/template-syntax.html#dynamically-binding-multiple-attributes) binds all the properties of an object as attributes of the target element.

## Attribute Inheritance on Multiple Root Nodes

Unlike single root node components, components with multiple root nodes do not have an automatic attribute fallthrough behavior. If `$attrs` are not bound explicitly, a runtime warning will be issued.
Unlike components with a single root node, components with multiple root nodes do not have an automatic attribute fallthrough behavior. If `$attrs` are not bound explicitly, a runtime warning will be issued.

```vue-html
<CustomLayout id="custom-layout" @click="changeValue" />
Expand Down
10 changes: 5 additions & 5 deletions src/guide/components/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The `.once` modifier is also supported on component event listeners:
<MyComponent @some-event.once="callback" />
```

Like components and props, event names provide an automatic case transformation. Notice we emitted a camelCase event, but is able to listen to it using a kebab-cased listener in the parent. As with [props casing](/guide/components/props.html#prop-name-casing), we recommend using kebab-cased event listeners in templates.
Like components and props, event names provide an automatic case transformation. Notice we emitted a camelCase event, but can listen for it using a kebab-cased listener in the parent. As with [props casing](/guide/components/props.html#prop-name-casing), we recommend using kebab-cased event listeners in templates.

:::tip
Unlike native DOM events, component emitted events do **not** bubble. You can only listen to the events emitted by a direct child component.
Expand All @@ -45,7 +45,7 @@ It's sometimes useful to emit a specific value with an event. For example, we ma
</button>
```

Then when we listen to the event in the parent, we can use an inline arrow function as the listener, which allows us to access the event argument:
Then, when we listen to the event in the parent, we can use an inline arrow function as the listener, which allows us to access the event argument:

```vue-html
<MyButton @increase-by="(n) => count += n" />
Expand Down Expand Up @@ -169,12 +169,12 @@ See also: [Typing Component Emits](/guide/typescript/options-api.html#typing-com
Although optional, it is recommended to define all emitted events in order to better document how a component should work. It also allows Vue to exclude known listeners from [fallthrough attributes](/guide/components/attrs.html#v-on-listener-inheritance).

:::tip
If a native event (e.g., `click`) is defined in the `emits` option, the listener will now only listen to component-emitted `click` event and no longer respond to native `click` events.
If a native event (e.g., `click`) is defined in the `emits` option, the listener will now only listen to component-emitted `click` events and no longer respond to native `click` events.
:::

## Events Validation

Similar to prop type validation, an emitted event can be validated if it is defined with the Object syntax instead of the array syntax.
Similar to prop type validation, an emitted event can be validated if it is defined with the object syntax instead of the array syntax.

To add validation, the event is assigned a function that receives the arguments passed to the `emit` call and returns a boolean to indicate whether the event is valid or not.

Expand Down Expand Up @@ -385,7 +385,7 @@ By default, `v-model` on a component uses `modelValue` as the prop and `update:m
<MyComponent v-model:title="bookTitle" />
```

In this case, child component should expect a `title` prop and emit `update:title` event to update the parent value:
In this case, the child component should expect a `title` prop and emit an `update:title` event to update the parent value:

<div class="composition-api">

Expand Down
6 changes: 3 additions & 3 deletions src/guide/components/props.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ We use [PascalCase for component tags](/guide/components/registration.html#compo

### Static vs. Dynamic Props

So far, you've seen props passed as static value, like in:
So far, you've seen props passed as static values, like in:

```vue-html
<BlogPost title="My journey with Vue" />
Expand Down Expand Up @@ -418,7 +418,7 @@ defineProps({
```

:::tip
Code inside the `defineProps()` argument **cannot access other variables declared in `<script setup>`**, because the entire expression is moved out to an outer function scoped when compiled.
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.
:::

</div>
Expand Down Expand Up @@ -483,7 +483,7 @@ If using [Type-based props declarations](/api/sfc-script-setup.html#typescript-o
<div class="options-api">

::: tip Note
Note that props are validated **before** a component instance is created, so instance properties (e.g. `data`, `computed`, etc) will not be available inside `default` or `validator` functions.
Note that props are validated **before** a component instance is created, so instance properties (e.g. `data`, `computed`, etc.) will not be available inside `default` or `validator` functions.
:::

</div>
Expand Down
12 changes: 6 additions & 6 deletions src/guide/components/provide-inject.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Props Drilling

Usually, when we need to pass data from the parent to child component, we use [props](/guide/components/props). However, imagine the case where we have a large component tree, and a deeply nested component needs something from a distant ancestor component. With only props, we would have to pass the same prop across the entire parent chain:
Usually, when we need to pass data from the parent to a child component, we use [props](/guide/components/props). However, imagine the case where we have a large component tree, and a deeply nested component needs something from a distant ancestor component. With only props, we would have to pass the same prop across the entire parent chain:

![props drilling diagram](./images/props-drilling.png)

Expand Down Expand Up @@ -46,7 +46,7 @@ export default {

The `provide()` function accepts two arguments. The first argument is called the **injection key**, which can be a string or a `Symbol`. The injection key is used by descendent components to lookup the desired value to inject. A single component can call `provide()` multiple times with different injection keys to provide different values.

The second argument is the provided value. The value can be of any type. including reactive state such as refs:
The second argument is the provided value. The value can be of any type, including reactive state such as refs:

```js
import { ref, provide } from 'vue'
Expand Down Expand Up @@ -193,7 +193,7 @@ Here, the component will locate a property provided with the key `"message"`, an

### Injection Default Values

By default, `inject` assumes that the injected key is provided somewhere in the parent chian. In the case where the key is not provided, there will be a runtime warning.
By default, `inject` assumes that the injected key is provided somewhere in the parent chain. In the case where the key is not provided, there will be a runtime warning.

If we want to make an injected property work with optional providers, we need to declare a default value, similar to props:

Expand Down Expand Up @@ -221,11 +221,11 @@ export default {
// when declaring default values for injections
inject: {
message: {
from: 'message', // this is optional is using the same key for injection
from: 'message', // this is optional if using the same key for injection
default: 'default value'
},
user: {
// make sure to use factory function for non-primitive values!
// make sure to use a factory function for non-primitive values!
default: () => ({ name: 'John' })
}
}
Expand All @@ -240,7 +240,7 @@ export default {

When using reactive provide / inject values, **it is recommended to keep any mutations to reactive state inside of the _provider_ whenever possible**. This ensures that the provided state and its possible mutations are co-located in the same component, making it easier to maintain in the future.

There may be times where we need to update the data from a injector component. In such cases, we recommend providing a method that is responsible for mutating the state:
There may be times when we need to update the data from a injector component. In such cases, we recommend providing a function that is responsible for mutating the state:

```vue{7-9,13}
<!-- inside provider component -->
Expand Down
14 changes: 7 additions & 7 deletions src/guide/components/registration.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ app
.component('ComponentC', ComponentC)
```

Globally registered components can be used in the template of any component instance within this application:
Globally registered components can be used in the template of any component within this application:

```vue-html
<!-- this will work in any component inside the app -->
Expand All @@ -55,15 +55,15 @@ This even applies to all subcomponents, meaning all three of these components wi

While convenient, global registration has a few drawbacks:

1. Global registration prevents build systems from removing unused components (a.k.a "tree-shaking"). If you globally register a component but ends up not using it anywhere in your app, it will still be included in the final bundle.
1. Global registration prevents build systems from removing unused components (a.k.a "tree-shaking"). If you globally register a component but end up not using it anywhere in your app, it will still be included in the final bundle.

2. Global registration makes dependency relationships less explicit in large applications. It makes it difficult to locate a child component's implementation from a parent component using it. This can affect long term maintainability similar to using too many global variables.
2. Global registration makes dependency relationships less explicit in large applications. It makes it difficult to locate a child component's implementation from a parent component using it. This can affect long-term maintainability similar to using too many global variables.

Local registration scopes the availability of the registered components to the current component only. It makes the dependency relationship more explicit, and is more tree-shaking-friendly.
Local registration scopes the availability of the registered components to the current component only. It makes the dependency relationship more explicit, and is more tree-shaking friendly.

<div class="composition-api">

When using SFC with `<script setup>`, imported components are automatically local-registered:
When using SFC with `<script setup>`, imported components are automatically registered locally:

```vue
<script setup>
Expand Down Expand Up @@ -93,7 +93,7 @@ export default {
</div>
<div class="options-api">

Local registration are done using the `components` option:
Local registration is done using the `components` option:

```vue
<script>
Expand Down Expand Up @@ -128,7 +128,7 @@ Note that **locally registered components are _not_ also available in descendent

## Component Name Casing

Throughout the guide, we are using PascalCase for component registration names. This is because:
Throughout the guide, we are using PascalCase names when registering components. This is because:

1. PascalCase names are valid JavaScript identifiers. This makes it easier to import and register components in JavaScript. It also helps IDEs with auto-completion.

Expand Down
29 changes: 14 additions & 15 deletions src/guide/components/slots.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For example, we may have a `<FancyButton>` component that supports usage like th
</FancyButton>
```

This is how the template of `<FancyButton>` looks like:
The template of `<FancyButton>` looks like this:

```vue-html{2}
<button class="fancy-btn">
Expand Down Expand Up @@ -124,7 +124,7 @@ We might want the text "Submit" to be rendered inside the `<button>` if the pare
</button>
```

Now when we use `<submit-button>` in a parent component, providing no content for the slot:
Now when we use `<SubmitButton>` in a parent component, providing no content for the slot:

```vue-html
<SubmitButton />
Expand Down Expand Up @@ -195,7 +195,7 @@ For these cases, the `<slot>` element has a special attribute, `name`, which can

A `<slot>` outlet without `name` implicitly has the name "default".

In a parent component using `<BaseLayout>`, we need a way to pass multiple slot content framgents, each targeting a different slot outlet. This is where **named slots** comes in.
In a parent component using `<BaseLayout>`, we need a way to pass multiple slot content fragments, each targeting a different slot outlet. This is where **named slots** come in.

To pass a named slot, we need to use a `<template>` element with the `v-slot` directive, and then pass the name of the slot as an argument to `v-slot`:

Expand All @@ -213,7 +213,7 @@ To pass a named slot, we need to use a `<template>` element with the `v-slot` di

<!-- https://www.figma.com/file/2BhP8gVZevttBu9oUmUUyz/named-slot -->

Here's the code passing content to all three slots to `<BaseLayout>` using the shorthand syntax:
Here's the code passing content for all three slots to `<BaseLayout>` using the shorthand syntax:

```vue-html
<BaseLayout>
Expand All @@ -232,7 +232,7 @@ Here's the code passing content to all three slots to `<BaseLayout>` using the s
</BaseLayout>
```

When a component accepts both default slot and named slots, all top-level non-`<template>` nodes are implciitly treated as content for default slot. So the above can also be written as:
When a component accepts both a default slot and named slots, all top-level non-`<template>` nodes are implicitly treated as content for the default slot. So the above can also be written as:

```vue-html
<BaseLayout>
Expand Down Expand Up @@ -317,7 +317,7 @@ function BaseLayout(slots) {
</base-layout>
```

Do note the expression is subject to the same [syntax constraints](/guide/essentials/template-syntax.html#directives) of dynamic directive arguments.
Do note the expression is subject to the [syntax constraints](/guide/essentials/template-syntax.html#directives) of dynamic directive arguments.

## Scoped Slots

Expand All @@ -337,9 +337,9 @@ In fact, we can do exactly that - we can pass attributes to a slot outlet just l
Receiving the slot props is a bit different when using a single default slot vs. using named slots. We are going to show how to receive props using a single default slot first, by using `v-slot` directly on the child component tag:

```vue-html
<MyComonent v-slot="slotProps">
<MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
<MyComponent>
</MyComponent>
```

<div class="composition-api">
Expand All @@ -353,7 +353,7 @@ Receiving the slot props is a bit different when using a single default slot vs.

</div>

The props passed to the slot by the child is available as the value of the corresponding `v-slot` directive, which can be accessed by expressions inside the slot.
The props passed to the slot by the child are available as the value of the corresponding `v-slot` directive, which can be accessed by expressions inside the slot.

You can think of a scoped slot as a function being passed into the child component. The child component then calls it and passing props as arguments:

Expand All @@ -378,13 +378,12 @@ function MyComponent(slots) {

In fact, this is very close to how scoped slots are compiled, and how you would use scoped slots in manual [render functions](/guide/extras/render-function.html).

Notice how `v-slot="slotProps"` matches the slot function signature - this means similar to function arguments, we can use destructuring in `v-slot`:

Notice how `v-slot="slotProps"` matches the slot function signature. Just like with function arguments, we can use destructuring in `v-slot`:

```vue-html
<MyComonent v-slot="{ text, count }">
<MyComponent v-slot="{ text, count }">
{{ text }} {{ count }}
<MyComponent>
</MyComponent>
```

### Named Scoped Slots
Expand Down Expand Up @@ -456,9 +455,9 @@ Inside `<FancyList>`, we can render the same `<slot>` multiple times with differ

The `<FancyList>` use case we discussed above encapsulates both reusable logic (data fetching, pagination etc.) and visual output, while delegating part of the visual output to the consumer component via scoped slots.

If we push this concept a bit further, we can come up with components that only encapsulate logic and do not render anything by themselves - visual output is fully delegated to the consumer component with scoped slots. We call this type of components **Renderless Components**.
If we push this concept a bit further, we can come up with components that only encapsulate logic and do not render anything by themselves - visual output is fully delegated to the consumer component with scoped slots. We call this type of component a **Renderless Component**.

An exmaple renderless component could be one that encapsulates the logic of tracking the current mouse position:
An example renderless component could be one that encapsulates the logic of tracking the current mouse position:

```vue-html
<MouseTracker v-slot="{ x, y }">
Expand Down
Loading