Skip to content

Commit a2b8470

Browse files
NataliaTepluhinantepluhinaznck
authored
Dynamic and async components (#49)
* Added dynamic components * Finished dynamic components * Update src/guide/component-dynamic-async.md Co-Authored-By: Rahul Kadyan <[email protected]> * Update src/guide/component-dynamic-async.md Co-Authored-By: Rahul Kadyan <[email protected]> * fix: added dynamic components link to sidebar * fix: refactored dynamic components * fix: updated anvanced usage * Update src/guide/component-dynamic-async.md Co-Authored-By: Rahul Kadyan <[email protected]> * fix: fixed keep-alive naming and explanation * Update src/guide/component-dynamic-async.md Co-Authored-By: Rahul Kadyan <[email protected]> * fix: fixed component local registration * fix: removed advanced usage * fix: removed keep-alive note Co-authored-by: ntepluhina <[email protected]> Co-authored-by: Rahul Kadyan <[email protected]>
1 parent c210a01 commit a2b8470

File tree

2 files changed

+128
-27
lines changed

2 files changed

+128
-27
lines changed

Diff for: src/.vuepress/config.js

+28-27
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ const sidebar = {
1414
'/guide/list',
1515
'/guide/events',
1616
'/guide/forms',
17-
'/guide/component-basics'
18-
]
17+
'/guide/component-basics',
18+
],
1919
},
2020
{
2121
title: 'Components In-Depth',
@@ -25,8 +25,9 @@ const sidebar = {
2525
'/guide/component-props',
2626
'/guide/component-custom-events',
2727
'/guide/component-slots',
28-
'/guide/component-provide-inject'
29-
]
28+
'/guide/component-provide-inject',
29+
'/guide/component-dynamic-async',
30+
],
3031
},
3132
{
3233
title: 'Reusability & Composition',
@@ -36,14 +37,14 @@ const sidebar = {
3637
{
3738
title: 'Migration to Vue 3',
3839
collapsable: true,
39-
children: ['migration']
40+
children: ['migration'],
4041
},
4142
{
4243
title: 'Contribute to the Docs',
4344
collapsable: true,
44-
children: ['writing-guide']
45-
}
46-
]
45+
children: ['writing-guide'],
46+
},
47+
],
4748
}
4849

4950
module.exports = {
@@ -55,9 +56,9 @@ module.exports = {
5556
{
5657
href:
5758
'https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
58-
rel: 'stylesheet'
59-
}
60-
]
59+
rel: 'stylesheet',
60+
},
61+
],
6162
],
6263
themeConfig: {
6364
nav: [
@@ -67,52 +68,52 @@ module.exports = {
6768
items: [
6869
{ text: 'Guide', link: '/guide/introduction' },
6970
{ text: 'Style Guide', link: '/style-guide/' },
70-
{ text: 'Tooling', link: '/tooling/' }
71-
]
71+
{ text: 'Tooling', link: '/tooling/' },
72+
],
7273
},
7374
{ text: 'API Reference', link: '/api/' },
7475
{
7576
text: 'Examples',
7677
ariaLabel: 'Examples Menu',
7778
items: [
7879
{ text: 'Examples', link: '/examples/' },
79-
{ text: 'Cookbook', link: '/cookbook/' }
80-
]
80+
{ text: 'Cookbook', link: '/cookbook/' },
81+
],
8182
},
8283
{
8384
text: 'Community',
8485
ariaLabel: 'Community Menu',
8586
items: [
8687
{ text: 'Team', link: '/community/team/' },
8788
{ text: 'Partners', link: '/community/partners/' },
88-
{ text: 'Themes', link: '/community/themes/' }
89-
]
90-
}
89+
{ text: 'Themes', link: '/community/themes/' },
90+
],
91+
},
9192
],
9293
sidebarDepth: 2,
9394
sidebar: {
9495
'/guide/': sidebar.guide,
95-
'/community/': sidebar.guide
96+
'/community/': sidebar.guide,
9697
},
97-
smoothScroll: false
98+
smoothScroll: false,
9899
},
99100
plugins: {
100101
'@vuepress/pwa': {
101102
serviceWorker: true,
102103
updatePopup: {
103104
'/': {
104105
message: 'New content is available.',
105-
buttonText: 'Refresh'
106-
}
107-
}
108-
}
106+
buttonText: 'Refresh',
107+
},
108+
},
109+
},
109110
},
110111
markdown: {
111112
/** @param {import('markdown-it')} md */
112-
extendMarkdown: md => {
113+
extendMarkdown: (md) => {
113114
md.options.highlight = require('./markdown/highlight')(
114115
md.options.highlight
115116
)
116-
}
117-
}
117+
},
118+
},
118119
}

Diff for: src/guide/component-dynamic-async.md

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Dynamic & Async Components
2+
3+
> This page assumes you've already read the [Components Basics](components.md). Read that first if you are new to components.
4+
5+
## Dynamic Components with `keep-alive`
6+
7+
Earlier, we used the `is` attribute to switch between components in a tabbed interface:
8+
9+
```vue-html
10+
<component v-bind:is="currentTabComponent"></component>
11+
```
12+
13+
When switching between these components though, you'll sometimes want to maintain their state or avoid re-rendering for performance reasons. For example, when expanding our tabbed interface a little:
14+
15+
<p class="codepen" data-height="300" data-theme-id="39028" data-default-tab="html,result" data-user="Vue" data-slug-hash="jOPjZOe" data-editable="true" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Dynamic components: without keep-alive">
16+
<span>See the Pen <a href="https://codepen.io/team/Vue/pen/jOPjZOe">
17+
Dynamic components: without keep-alive</a> by Vue (<a href="https://codepen.io/Vue">@Vue</a>)
18+
on <a href="https://codepen.io">CodePen</a>.</span>
19+
</p>
20+
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
21+
22+
You'll notice that if you select a post, switch to the _Archive_ tab, then switch back to _Posts_, it's no longer showing the post you selected. That's because each time you switch to a new tab, Vue creates a new instance of the `currentTabComponent`.
23+
24+
Recreating dynamic components is normally useful behavior, but in this case, we'd really like those tab component instances to be cached once they're created for the first time. To solve this problem, we can wrap our dynamic component with a `<keep-alive>` element:
25+
26+
```vue-html
27+
<!-- Inactive components will be cached! -->
28+
<keep-alive>
29+
<component v-bind:is="currentTabComponent"></component>
30+
</keep-alive>
31+
```
32+
33+
Check out the result below:
34+
35+
<p class="codepen" data-height="300" data-theme-id="39028" data-default-tab="html,result" data-user="Vue" data-slug-hash="VwLJQvP" data-editable="true" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Dynamic components: with keep-alive">
36+
<span>See the Pen <a href="https://codepen.io/team/Vue/pen/VwLJQvP">
37+
Dynamic components: with keep-alive</a> by Vue (<a href="https://codepen.io/Vue">@Vue</a>)
38+
on <a href="https://codepen.io">CodePen</a>.</span>
39+
</p>
40+
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
41+
42+
Now the _Posts_ tab maintains its state (the selected post) even when it's not rendered.
43+
44+
Check out more details on `<keep-alive>` in the [API reference](TODO:../api/#keep-alive).
45+
46+
## Async Components
47+
48+
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` method:
49+
50+
```js
51+
const app = Vue.createApp({})
52+
53+
const AsyncComp = Vue.defineAsyncComponent(
54+
() =>
55+
new Promise((resolve, reject) => {
56+
resolve({
57+
template: '<div>I am async!</div>'
58+
})
59+
})
60+
)
61+
62+
app.component('async-example', AsyncComp)
63+
```
64+
65+
As you can see, this method accepts a factory function returning a `Promise`. 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.
66+
67+
You can also return a `Promise` in the factory function, so with Webpack 2 or later and ES2015 syntax you can do:
68+
69+
```js
70+
import { defineAsyncComponent } from 'vue'
71+
72+
const AsyncComp = defineAsyncComponent(() =>
73+
import('./components/AsyncComponent.vue')
74+
)
75+
76+
app.component('async-component', AsyncComp)
77+
```
78+
79+
You can also use `defineAsyncComponent` when [registering a component locally](components-registration.html#Local-Registration):
80+
81+
```js
82+
import { createApp, defineAsyncComponent } from 'vue'
83+
84+
createApp({
85+
// ...
86+
components: {
87+
AsyncComponent: defineAsyncComponent(() =>
88+
import('./components/AsyncComponent.vue')
89+
)
90+
}
91+
})
92+
```
93+
94+
### Using with Suspense
95+
96+
Async components are _suspensible_ by default. This means if it has a [`<Suspense>`](TODO) in the parent chain, it will be treated as an async dependency of that `<Suspense>`. In this case, the loading state will be controlled by the `<Suspense>`, and the component's own loading, error, delay and timeout options will be ignored.
97+
98+
The async component can opt-out of `Suspense` control and let the component always control its own loading state by specifying `suspensible: false` in its options.
99+
100+
You can check the list of available options in the [API Reference](TODO)

0 commit comments

Comments
 (0)