十、组件(5)

本章概要

  • 动态组件
  • 异步组件

10.7 动态组件

在页面应用程序中,经常会遇到多标签页面,在Vue.js 中,可以通过动态组件来实现。组件的动态切换时通过在 component 元素上使用 is 属性来实现的。

app.component('tab-introduce', {
  data() {
    return {
      content: '红楼梦'
    }
  },
  template: '<div><input v-model="content"></div>'
})
app.component('tab-comment', {
  template: '<div>这是一本好书</div>'
})
app.component('tab-qa', {
  template: '<div>有人看过吗?怎么样?</div>'
})

组件的模板使用了一个 input 元素,便于修改内容。
在根实例中定义了两个数据属性和一个计算属性,主要是为了便于使用 v-for 指令循环渲染 button 按钮,以及动态切换组件。

const app = Vue.createApp({
  data() {
    return {
      currentTab: 'introduce',
      tabs: [
        { title: 'introduce', displayName: '图书介绍' },
        { title: 'comment', displayName: '图书评价' },
        { title: 'qa', displayName: '图书问答' }
      ]
    }
  },
  computed: {
    currentTabComponent: function () {
      return 'tab-' + this.currentTab
    }
  }
})
...
app.mount('#app');

数据属性 currentTab 代表当前的标签页,tabs 是一个数组对象,通过 v-for 指令渲染代表标签的 3 个按钮,计算属性 currentTabComponent 代表当前选中的组件。
接下来就是在与实例关联的 DOM 模板中渲染按钮,以及动态切换组件的代码。

<div id="app">
  <button v-for="tab in tabs" :key="tab.title" :class="['tab-button', { active: currentTab === tab.title }]"
    @click="currentTab = tab.title">
    {{ tab.displayName }}
  </button>
  <keep-alive>
    <component :is="currentTabComponent" class="tab">
    </component>
  </keep-alive>
</div>

当点击某个标签按钮时,更改数据属性 currentTab 的值,这将导致计算属性 currentTabComponent 的值更新。
component 元素的 is 属性使用 v-bind 指令绑定到一个已注册的名字上,随着计算属性 currentTabComponent 值的改变,组件也就自动切换了。
剩下的代码就是 CSS 样式的设置了。完整代码如下:

<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<title>动态组件</title>
	<style>
		div {
			width: 400px;
		}

		.tab-button {
			padding: 6px 10px;
			border-top-left-radius: 3px;
			border-top-right-radius: 3px;
			border: solid 1px #ccc;
			cursor: pointer;
			background: #f0f0f0;
			margin-bottom: -1px;
			margin-right: -1px;
		}

		.tab-button:hover {
			background: #e0e0e0;
		}

		.tab-button .active {
			background: #cdcdcd;
		}

		.tab {
			border: solid 1px #ccc;
			padding: 10px;
		}
	</style>
</head>

<body>
	<div id="app">
		<button v-for="tab in tabs" :key="tab.title" :class="['tab-button', { active: currentTab === tab.title }]"
			@click="currentTab = tab.title">
			{{ tab.displayName }}
		</button>
			<component :is="currentTabComponent" class="tab">
			</component>
	</div>

	<script src="https://unpkg.com/vue@next"></script>
	<script>
		const app = Vue.createApp({
			data() {
				return {
					currentTab: 'introduce',
					tabs: [
						{ title: 'introduce', displayName: '图书介绍' },
						{ title: 'comment', displayName: '图书评价' },
						{ title: 'qa', displayName: '图书问答' }
					]
				}
			},
			computed: {
				currentTabComponent: function () {
					return 'tab-' + this.currentTab
				}
			}
		})
		app.component('tab-introduce', {
			data() {
				return {
					content: '红楼梦'
				}
			},
			template: '<div><input v-model="content"></div>'
		})
		app.component('tab-comment', {
			template: '<div>这是一本好书</div>'
		})
		app.component('tab-qa', {
			template: '<div>有人看过吗?怎么样?</div>'
		})

		app.mount('#app');
	</script>
</body>

</html>

修改图书介绍的内容,修改后切换到其它标签页,然后再切换过来,发现之前修改的内容并没保存下来。
如下:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这是因为每次切换新标签的时候,Vue 都创建一个新的 currentTabComponent 实例。
在本例中,希望组件在切换的时候,可以保持组件的状态,以避免重复渲染导致的性能问题,也为了让用户体验更好。
要解决这个问题,可以用一个 keep-alive 元素将动态组件包裹起来。如下:

<keep-alive>
  <component :is="currentTabComponent" class="tab">
  </component>
</keep-alive>

10.8 异步组件

在大型的应用中,可能需要将应用分割成较小的代码块,并且只在需要时才从服务器加载组件。
为了实现这一点,Vue 给出了一个 defineAsyncComponent() 方法,该方法接受一个返回 Promise 的工厂函数,当从服务器检索到组件定义的时候,应该调用 Promise 的 resolve 回调。如下:

const app = Vue.createApp({})
const AsyncComp = Vue.defineAsyncComponent(
    ()=> new Promise((resolve,reject)=>{
        resolve({
            template:'<div>只有头发不绿的浩克</div>'
        })
    })
)
app.component('async-example',AsyncComp);

当然也可以调用 reject(reason) 指示加载失败。
也可以在工厂函数中返回一个 Promise ,因此对于 Webpack 2 或更高版本,以及 ES6 语法,可以执行一下操作。

import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(()=>
    import('./components/AsyncComponent.vue')
)
app.component('async-component',AsyncComp)

在本地注册组件时,也可以使用 defineAsyncComponent() 方法。如下:

import { defineAsyncComponent } from 'vue'
createApp({
    //...
    components:{
        AsyncComponent:defineAsyncComponent(()=>
        import('./components/AsyncComponent.vue')
        )
    }
})

defineAsyncComponent() 方法还可以接受一个对象作为参数。如下:

import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
    // 工厂函数
    loder:()=>import('./Foo.vue'),
    // 加载异步组件时要使用的组件
    loadingComponent:loadingComponent,
    // 加载失败时要使用的组件
    errorComponent:ErrorComponent,
    // 显示加载组件前的延迟时间,默认是 200ms
    delay:200,
    // 如果给出了超时值并超过超时值,则显示错误组件。默认没有超时值
    timeout:3000,
    // 定义组件是否可悬挂,默认为true
    suspensible:false,
    /**
     * @param {*} error 错误消息对象
     * @param {*} retry 指示当加载器 promise 拒绝时异步组件是否重试的函数
     * @param {*} fail 失败结束
     * @param {*} 允许重试的最大尝试次数
     * 
     */
    onerror(error,retry,fail,attempts){
        if(error.message.math(/fetch/) && attempts <= 3 ){
            // 获取错误时重试,最多尝试 3 次
            retry()
        }else{
            // 请注意,重试/失败类似于 promise 的 resolve / reject
            // 要继续进行错误处理,必须调用其中的一个
            fail();
        }
    }
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小熊猫呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值