【vue学习】动态组件和异步组件
动态组件有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:is上述内容可以通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现<component v-bind:is="currentTabComponent"></component>在上述示例中,currentTabComponent 可以包...
动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
is
上述内容可以通过 Vue
的 <component>
元素加一个特殊的 is
特性来实现
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent
可以包括
- 已注册组件的名字,或
- 一个组件的选项对象
component
中绑定参数的方式和普通组件一样
- 解析
DOM
模板时的注意事项:有些HTML
元素,诸如<ul>、<ol>、<table>
和<select>
,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如<li>、<tr>
和<option>
,只能出现在其它某些特定的元素内部。这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table>
<blog-post-row></blog-post-row>
</table>
- 这个自定义组件
<blog-post-row>
会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的is
特性给了我们一个变通的办法:
<table>
<tr is="blog-post-row"></tr>
</table>
keep-alive
当组件切换时,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题
上图中,你选择了一篇文章,切换到 Archive
标签,然后再切换回 Posts
,不会继续展示之前选择的文章。因为你每次切换时,Vue
都创建了一个新的 currentTabComponent
实例。
重新创建动态组件的行为通常非常有用,但该案例中,我们更希望标签的组件实例能够被缓存下来。我们可以用 <keep-alive>
元素将其动态组件包裹起来。
注意这个
<keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的name
选项还是局部/全局注册。
组件中的name选项有什么作用?
根据上面的注意事项,延伸出该问题,下面来看个网上的回答:
- 当项目使用keep-alive时,可搭配组件name进行缓存过滤
//假设我们有个组件命名为detail,
//其中dom加载完毕后我们在钩子函数mounted中进行数据加载
export default {
name:'Detail'
},
mounted(){
this.getInfo();
},
methods:{
getInfo(){
axios.get('/xx/detail.json',{
params:{
id:this.$route.params.id
}
}).then(this.getInfoSucc)
}
}
/**
在App.vue中使用了keep-alive导致
我们第二次进入的时候页面不会重新请求,
即不会再次触发mounted函数。
有两个解决方案:
一个增加activated()函数,每次进入新页面的时候再获取一次数据。
还有个方案就是在keep-alive中增加一个过滤exclude:
*/
<div id="app">
<keep-alive exclude="Detail">
<router-view/>
</keep-alive>
</div>
- DOM做递归组件时
<div>
<div v-for="(item,index) of list" :key="index">
<div>
<span class="item-title-icon"></span>
{{item.title}}
</div>
<div v-if="item.children" >
<!-- detail-list 就是该组件自身 -->
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
<script>
export default {
name:'DetailList',//递归组件是指组件自身调用自身
props:{
list:Array
/**
const list = [{
"title": "A",
"children": [
{"title": "A-A","children": [{"title": "A-A-A"}]},
{"title": "A-B"}]
}, {
"title": "B"
}, {
"title": "C"
}, {
"title": "D"
}]
*/
}
}
</script>
迭代结果如下:
3. 当你用vue-tools
时:该调试工具里显示的组见名称是由vue
中组件name
决定的
异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。
为了简化,
Vue
允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。
Vue
只有在这个组件需要被渲染时才会触发该工厂函数,且会把结果缓存起来供未来重渲染
知识点
-
如你所见,这个工厂函数会收到一个
resolve
回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用reject(reason)
来表示加载失败。 -
这里的
setTimeout
是为了演示用的,如何获取组件取决于你自己。一个推荐的做法是将异步组件和webpack
的code-splitting
功能一起配合使用:
-
你也可以在工厂函数中返回一个
Promise
,所以把webpack 2
和ES2015
语法加在一起,我们可以写成这样:
组件之间的循环引用
-
当使用局部注册的时候,你也可以直接提供一个返回
Promise
的函数:
-
vue
处理边界情况中有一种情况是:组件之间的循环引用,这里就涉及到了异步加载组件问题:
-
当你仔细观察的时候,你会发现这些组件在渲染树中互为对方的后代和祖先——一个悖论!
当通过Vue.component
全局注册组件的时候,这个悖论会被自动解开。
如果你是这样做的,那么你可以跳过这里。 -
然而,如果你使用一个模块系统依赖/导入组件,例如通过
webpack
或Browserify
,你会遇到一个错误:
-
这里我们可以异步
import
(具体的我们可以在处理边界情况一节中具体再学习)
处理加载状态
2.3.0+
新增:这块的具体应用后续再更新…
这里的异步组件工厂函数也可以返回一个如下格式的对象:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
注意如果你希望在
Vue Router
的路由组件中使用上述语法的话,你必须使用Vue Router 2.4.0+
版本。
Vue异步组件处理路由组件加载状态的解决方案
- 在大型单页面应用中,处于对性能的考虑和首屏加载速度的要求,我们一般都会使用
webpack
的代码分割和vue-router
的路由懒加载功能将我们的代码分成一个个模块,并且只在需要的时候才从服务器加载一个模块。 - 但是这种解决方案也有其问题,当网络环境较差时,我们去首次访问某个路由模块,由于加载该模块的资源需要一定的时间,那么该段时间内,我们的应用就会处于无响应的状态,用户体验极差。
- 【解决方案】这种情况,我们一方面可以缩小路由模块代码的体积,静态资源使用
cdn
存储等方式缩短加载时间,另一方面则可以路由组件上使用异步组件,显示loading
和error
等状态,使用户能够得到清晰明了的操作反馈。 - 【具体实现】声明方法,基于
Vue
动态组件工厂函数来返回一个Promise
对象
调用:
更多推荐
所有评论(0)