动态组件和异步组件

本页面会假定你已经阅读过 组件基础。如果你还不熟悉组件,请先阅读组件基础后再阅读本页面。

对动态组件使用 keep-alive

前面,我们使用 is 特性,在标签页式界面中切换组件:

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

有时在这些组件之间切换,需要保留切换前的状态,或者需要考虑重新渲染造成的性能问题,而尽量避免重新渲染。举例说明,稍微扩展下我们的标签页式界面:

然后你会注意到,如果选中某篇文章(post),切换到 Archive 标签页,再切换回 Posts 标签页,而选取的文章则不会再次展现。这是因为,每次你切换到一个新的标签页后,Vue 都会创建 currentTabComponent 所对应组件的一个全新实例。

重新创建动态组件是一种常规用法,但是在这个场景中,我们确实希望这些标签页组件的实例,在第一次创建之后就缓存起来。为了解决这个问题,我们可以使用一个 <keep-alive> 元素将动态组件包裹起来:

<!-- Inactive components will be cached! -->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>

查看下面结果:

现在 Posts 标签页并未渲染,而是保持它先前(选中那篇文章)的状态。完整代码请查看 fiddle

注意,<keep-alive> 要求被切换的组件都要具有 name,要么是使用组件的 name 选项,要么就是通过局部/全局注册。

更多细节请查看 API 参考<keep-alive> 部分。

异步组件

在大型应用程序中,我们可能需要将应用程序拆分为多个小的分块(chunk),并且只在实际用到时,才从服务器加载某个组件。为了简化异步按需加载组件机制,Vue 能够将组件定义为一个工厂函数(factory function),此函数可以异步地解析(resolve)组件定义对象(component definition)。Vue 只在真正需要渲染组件时,才会去触发工厂函数,并且将解析后的结果缓存,用于将来再次渲染。例如:

Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 将组件定义对象(component definition)传递给 resolve 回调函数
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})

就像你看到的那样,工厂函数接收一个 resolve 回调函数,在从服务器获取到组件定义对象时,会调用此回调函数。也可以调用 reject(reason) 来表明加载失败。这里的 setTimeout 只是为了用于演示;如果获取组件定义对象,取决于你的实际情况。使用异步组件,比较推荐的方式是配合 webpack 代码分离功能

Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 require 语法
// 将指示 webpack 自动将构建后的代码,
// 拆分到不同的 bundle 中,然后通过 Ajax 请求加载。
require(['./my-async-component'], resolve)
})

还可以在工厂函数中返回一个 Promise,所以配合 webpack 2 + ES2015 语法,你可以这样实现:

Vue.component(
'async-webpack-example',
// `import` 函数返回一个 Promise.
() => import('./my-async-component')
)

当使用 局部注册 时,你也可以直接提供一个返回 Promise 的函数:

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

如果你是想要使用异步组件的 Browserify 用户,不幸的是,它的作者已经 明确表示 Browserify 是不支持异步加载的。虽然官方如此表示,然而 Browserify 社区还是找到 一些解决方案,这可能有助于现有的复杂应用程序实现异步组件。对于所有其他场景,我们推荐使用 webpack 内置和优先支持的异步支持。

处理加载状态

2.3.0+ 新增

异步组件工厂函数,还可以返回一个以下格式的对象:

const AsyncComponent = () => ({
// 加载组件(最终应该返回一个 Promise)
component: import('./MyComponent.vue'),
// 异步组件加载中(loading),展示为此组件
loading: LoadingComponent,
// 加载失败,展示为此组件
error: ErrorComponent,
// 展示 loading 组件之前的延迟时间。默认:200ms。
delay: 200,
// 如果提供 timeout,并且加载用时超过此 timeout,
// 则展示错误组件。默认:Infinity。
timeout: 3000
})

注意,如果你想要在路由组件中使用以上语法,必须使用 Vue Router 2.4.0+ 版本。