Skip to content

Moving Components Page into its Own Category #1482

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 42 commits into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b5cd524
create components sidebar
sdras Mar 10, 2018
38ae34c
break up first two sections
sdras Mar 10, 2018
c816498
break it up further
sdras Mar 10, 2018
0a228f5
split up the rest
sdras Mar 10, 2018
12a4b9c
clean up
sdras Mar 10, 2018
7a42b83
initial pass, creating new, much slimmer components intro page that l…
chrisvfritz Mar 12, 2018
65d65c0
finish initial component registration page
chrisvfritz Mar 13, 2018
81f60f2
finish initial refactor of component props page
chrisvfritz Mar 14, 2018
e8ab0ec
beginning work on components custom events page
chrisvfritz Mar 14, 2018
594b1d0
finish initial components custom events page
chrisvfritz Mar 14, 2018
7905383
finish initial components slots page
chrisvfritz Mar 15, 2018
e63512c
finish initial dynamic and async components page
chrisvfritz Mar 15, 2018
8389490
finish initial dynamic-async and edge-cases components pages
chrisvfritz Mar 16, 2018
3f9d882
components review and tweaks up to the props page
chrisvfritz Mar 17, 2018
c37c9d0
fix components mistakes found by @phanan
chrisvfritz Mar 17, 2018
4fae206
fix typo in components-edge-cases
chrisvfritz Mar 17, 2018
c1af324
reorder Compiliation Scope in components-slots
chrisvfritz Mar 17, 2018
57ee320
fix link in components basics
chrisvfritz Mar 17, 2018
c329098
fix codeblock language typo in components
chrisvfritz Mar 17, 2018
60e5c25
add example of local registration in es2015 modules
chrisvfritz Mar 17, 2018
349247c
add keep-alive example
chrisvfritz Mar 17, 2018
4301124
remove redundant word in components
chrisvfritz Mar 17, 2018
512d8cc
make dom template parsing caveats h2
chrisvfritz Mar 17, 2018
869e4dd
change simple example to base example in components
chrisvfritz Mar 17, 2018
ce9b9e9
add async import example to circular references
chrisvfritz Mar 17, 2018
40731be
fix typo in components-custom-events
chrisvfritz Mar 18, 2018
070bfb8
remove redundand language in componenents-custom-events
chrisvfritz Mar 18, 2018
f7ee91c
add missing comma in components-custom-events
chrisvfritz Mar 18, 2018
1db36a0
restate context in component-dynamic-async
chrisvfritz Mar 18, 2018
7901ad1
remove redundant actuallys in components-dynamic-async
chrisvfritz Mar 18, 2018
e3d6f7d
clarify .sync modifier usage
chrisvfritz Mar 19, 2018
1d90cba
set up hash redirects for all old components sections
chrisvfritz Mar 19, 2018
ed09a59
remove note about intentional single root elements
chrisvfritz Mar 22, 2018
62b6cdf
fix dom template parsing caveats example
chrisvfritz Mar 22, 2018
6ccbbe6
fix closing </code> tags in components-edge-cases
chrisvfritz Mar 22, 2018
062afe3
fix typo in components-edge-cases
chrisvfritz Mar 22, 2018
7194e29
fix typo in components-edge-cases
chrisvfritz Mar 22, 2018
f858355
remove extra 'a' in components-registration
chrisvfritz Mar 22, 2018
59ec1e4
add name casing and various tweaks to components-registration
chrisvfritz Mar 23, 2018
b398f94
complete custom v-model example in components-custom-events
chrisvfritz Mar 23, 2018
ab0b0f5
minor tweaks to components pages
chrisvfritz Mar 23, 2018
0d06bd3
rename signature to definition for clarity
chrisvfritz Mar 23, 2018
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vuejs.org",
"private": true,
"hexo": {
"version": "3.4.2"
"version": "3.6.0"
},
"scripts": {
"start": "hexo server",
Expand Down
2 changes: 1 addition & 1 deletion src/v2/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1426,7 +1426,7 @@ type: api

- **Details:**

Contains parent-scope `v-on` event listeners (without `.native` modifiers). This can be passed down to an inner component via `v-on="$listeners"` - useful when creating higher-order components.
Contains parent-scope `v-on` event listeners (without `.native` modifiers). This can be passed down to an inner component via `v-on="$listeners"` - useful when creating transparent wrapper components.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we've talked about this before, are we all ok with transparent wrapper instead of higher-order? I think higher-order is more commonly used so it's possible it will be understood much faster by people learning, but I could go both ways on it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this different from an HOC? The definition used by React is:

a function that takes a component and returns a new component

This is a component rather than a function and it returns raw elements rather than a new component.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily, from the same document just a paragraph lower:

a higher-order component transforms a component into another component.

Having worked with both, I'm not sure I'd make such a distinction, but if you think that it's helpful for understanding, I'm all for it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please correct me if I'm still missing something, 😅 but I don't think this scenario fits that definition either though. Inside this component, we're wrapping a specific element to create a custom version of it, rather than wrapping or transforming a component. Does that make sense?

(Btw, I do agree with you that we should mention the vocabulary of HOCs when we use that pattern, I'm just not sure this is an example of it.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!


## Instance Methods / Data

Expand Down
158 changes: 158 additions & 0 deletions src/v2/guide/components-custom-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
---
title: Custom Events
type: guide
order: 103
---

> This page assumes you've already read the [Components Basics](components.html). Read that first if you are new to components.

## Event Names

Unlike components and props, event names don't have provide any automatic case transformation. Instead, the name of an emitted event must exactly match the name used to listen to that event. For example, if emitting a camelCased event name:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event names don't have provide any automatic case

have should be dropped here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. 👍


```js
this.$emit('myEvent')
```

Listening to the kebab-cased version will have no effect:

```html
<my-component v-on:my-event="doSomething"></my-component>
```

The reason for this is that unlike components and props, event names will never be used as variable or property names in JavaScript, so there's no reason to use camelCase or PascalCase. Additionally, `v-on` event listeners inside DOM templates will be automatically transformed to lowercase (due to HTML's case-insensitivity), so `v-on:myEvent` would become `v-on:myevent` -- making `myEvent` impossible to listen to.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this is that uUnlike components and props, event names will never be used as variable or property names in JavaScript, so there's no reason to use camelCase or PascalCase.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it! Updated. 🙂


For these reasons, we recommend you **always use kebab-case for event names**.

## Customizing Component `v-model`

> New in 2.2.0+

By default, `v-model` on a component uses `value` as the prop and `input` as the event, but some input types such as checkboxes and radio buttons may want to use the `value` attribute for a [different purpose](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#Value). Using the `model` option can avoid a conflict in such cases:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can avoid a conflict in such cases

The indefinite article "a" is unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave this in actually, because while many dialects of English would allow omitting the "a", I think it would sound strange in American English.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About to say the same. How is "a" unnecessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@znck can correct me if I'm wrong, but I think in Indian English it's more common, and even in American English there will be no article in some contexts. For example, "there was conflict between two countries."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in American English, omitting the a in this case would sound strange.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I often (read: almost always) rely on Grammarly for this, and it seems to dislike omitting the article in @chrisvfritz's example:

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@phanan 👍 for Grammarly - great tool! I do disagree with it sometimes though. 😄 I think this is a phrase people use on the news quite often.


``` js
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.value)"
>
`
})
```

<p class="tip">Note that you still have to declare the `checked` prop in `props`.</p>

## Binding Native Events to Components

There may be times when you want to listen directly to a native event on the root element of a component. In these cases, you can use the `.native` modifier for `v-on`:

```html
<base-input v-on:focus.native="onFocus"></base-input>
```

This can be useful sometimes, but it's not a good idea when you're trying to listen on a very specific element, like an `<input>`. For example, the `<base-input>` component above might refactor so that the root element is actually a `<label>` element:

```html
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
```

In that case, the `.native` listener in the parent would silently break. There would be no errors, but the `onFocus` handler wouldn't be called when we expected it to.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There would be no errors, but the onFocus handler wouldn't be called when we expected it to.

Can we simplify this statement?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, nothing is coming to mind, but I'm definitely open to suggestions!


To solve this problem, Vue provides a `$listeners` property containing an object of listeners being used on the component. For example:

```js
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
```

Using the `$listeners` property, you can forward all event listeners on the component to a specific child element with `v-on="$listeners"`. For elements like `<input>`, that you also want to work with `v-model`, it's often useful to create a new computed property for listeners, like `inputListeners` below:

```js
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` merges objects together to form a new object
return Object.assign({},
// We add all the listeners from the parent
this.$listeners,
// Then we can add custom listeners or override the
// behavior of some listeners.
{
// This ensures that the component works with v-model
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
```

Now the `<base-input>` component is a **fully transparent wrapper**, meaning it can be used exactly like a normal `<input>` element: all the same attributes and listeners will work.

## `.sync` Modifier

> New in 2.3.0+

In some cases we may need "two-way binding" for a prop. Unfortunately, true two-way binding can create maintenance issues, because child components can mutate the parent without the source of that mutation being obvious in both the parent and the child.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some cases, we

A missing coma.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. 👍


That's why instead, we recommend emitting events in the pattern of `update:my-prop-name`. For example, in a hypothetical component with a `title` prop, we could communicate the intent of assigning a new value with:

```js
this.$emit('update:title', newTitle)
```

Then the parent can listen to that event and update a local data property, if it wants to. For example:

```html
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
```

For convenience, we offer a shorthand for this pattern with the `.sync` modifier:

```html
<text-document v-bind:title.sync="doc.title"></text-document>
```

The `.sync` modifier can also be used with `v-bind` when using an object to set multiple props at once:

```html
<text-document v-bind.sync="doc"></text-document>
```

This has the effect of adding `v-on` update listeners for not only `title`, but also any other properties on the `doc` object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of This, we can repeat the context.

The .sync modifier when used with v-bind has the effect of ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this seems redundant to me since we just established the context in the previous sentence. @sdras What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does code snippets between paragraphs break context?

For large code snippets, it happens. I tend to go back to last line before continuing to next paragraph. For one line snippet, I am not sure. Moreover, the amount code breaking reading context may vary from person to person.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer "This" here. One line of code in between is not enough to repeat the statement.

Copy link
Contributor

@chrisvfritz chrisvfritz Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@znck Yeah, I agree with the principle in general, but in this particular case I think it will be better for most people to leave it out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I read it through and it seemed redundant to change it, it's pretty clear to me what this refers to. But that's a good thing to look out for!

101 changes: 101 additions & 0 deletions src/v2/guide/components-dynamic-async.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
title: Dynamic & Async Components
type: guide
order: 105
---

> This page assumes you've already read the [Components Basics](components.html). Read that first if you are new to components.

## `keep-alive` with Dynamic Components

Earlier, we used the `is` attribute to switch between components in a tabbed interface:

```html
<component v-bind:is="currentTabComponent"></component>
```

When switching between these components though, you'll sometimes want to maintain their state or avoid re-rendering for performance reasons. In these cases, you can wrap a dynamic component with a `<keep-alive>` element:

``` html
<!-- Inactive components will be cached! -->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, and the API don't fully show off the benefits of keep-alive, IMO, but that's out of scope of this PR, so I've filed an issue for myself to take care of it when this is merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and think that's a great idea!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't help myself and added it. 😅 Your idea was too good!

```

<p class="tip">Note that `<keep-alive>` requires the components being switched between to all have names, either using the `name` option on a component, or through local/global registration.</p>

Check out more details on `<keep-alive>` in the [API reference](../api/#keep-alive).

## Async Components

In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it's actually needed. To make that easier, Vue allows you to define your component as a factory function that asynchronously resolves your component definition. Vue will only trigger the factory function when the component actually needs to be rendered and will cache the result for future re-renders. For example:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove actually word from this paragraph as the meaning of the statement remains same.

...only load a component from the server when it's actually needed.
...when the component actually needs to be rendered and will cache the result...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it! Removed. 🙂


``` js
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the resolve callback
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
```

As you can see, the factory function receives a `resolve` callback, which 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. The `setTimeout` here is for demonstration; how to retrieve the component is up to you. One recommended approach is to use async components together with [Webpack's code-splitting feature](https://webpack.js.org/guides/code-splitting/):

``` js
Vue.component('async-webpack-example', function (resolve) {
// This special require syntax will instruct Webpack to
// automatically split your built code into bundles which
// are loaded over Ajax requests.
require(['./my-async-component'], resolve)
})
```

You can also return a `Promise` in the factory function, so with Webpack 2 and ES2015 syntax you can do:

``` js
Vue.component(
'async-webpack-example',
// The `import` function returns a Promise.
() => import('./my-async-component')
)
```

When using [local registration](components.html#Local-Registration), you can also directly provide a function that returns a `Promise`:

``` js
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
```

<p class="tip">If you're a <strong>Browserify</strong> user that would like to use async components, its creator has unfortunately [made it clear](https://github.com/substack/node-browserify/issues/58#issuecomment-21978224) that async loading "is not something that Browserify will ever support." Officially, at least. The Browserify community has found [some workarounds](https://github.com/vuejs/vuejs.org/issues/620), which may be helpful for existing and complex applications. For all other scenarios, we recommend using Webpack for built-in, first-class async support.</p>

### Handling Loading State

> New in 2.3.0+

The async component factory can also return an object of the following format:

``` js
const AsyncCompontent = () => ({
// The component to load (should be a Promise)
component: import('./MyComponent.vue'),
// A component to use while the async component is loading
loading: LoadingComponent,
// A component to use if the load fails
error: ErrorComponent,
// Delay before showing the loading component. Default: 200ms.
delay: 200,
// The error component will be displayed if a timeout is
// provided and exceeded. Default: Infinity.
timeout: 3000
})
```

> Note that you must use [Vue Router](https://github.com/vuejs/vue-router) 2.4.0+ if you wish to use the above syntax for route components.
Loading