文章目录
1. 重点提炼
- 动态组件
<component></component>
<keep-alive></keep-alive>
- 生命周期
- 冻结
- 激活
2. 动态组件
有的时候,我们需要在多个不同的组件之间进行切换(类似选项卡的效果)。虽然我们可以通过 v-if 来处理,但是会比较麻烦,vue
提供了一个更方便的方式来处理这种情况。
3. component 组件
component
是 vue
内置的一个组件,它提供一个 is
属性用来动态渲染不同的组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.current {
background: yellow;
}
</style>
</head>
<body>
<div id="app">
<button @click="goto('InBox')" :class="{
'current': currentComponent==='InBox'}">收邮件</button>
<button @click="goto('PostMail')" :class="{
'current': currentComponent==='PostMail'}">发邮件</button>
<button @click="goto('RecycleBin')" :class="{
'current': currentComponent==='RecycleBin'}">垃圾箱</button>
<hr>
<component :is="currentComponent"></component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const InBox = {
data() {
return {
items: [
'111111',
'22222222222',
'aaaaaaaa',
'3333333'
]
}
},
template: `
<div>
<ul>
<li v-for="item of items">
<input type="checkbox" />
{
{item}}
</li>
</ul>
</div>
`,
created() {
console.log('InBox:created');
},
destroyed() {
console.log('InBox:destroyed');
}
}
const PostMail = {
template: `
<div>PostMail</div>
`,
created() {
console.log('PostMail:created');
},
destroyed() {
console.log('PostMail:destroyed');
}
}
const RecycleBin = {
template: `
<div>RecycleBin</div>
`,
created() {
console.log('RecycleBin:created');
},
destroyed() {
console.log('RecycleBin:destroyed');
}
}
let app = new Vue({
el: '#app',
data: {
currentComponent: 'InBox'
},
components: {
InBox,
PostMail,
RecycleBin
},
methods: {
goto(target) {
this.currentComponent = target;
}
}
});
</script>
</body>
</html>
我们会发现,当组件切换的时候,都会触发组件的销毁和重建。
首先,性能不好。
其次,会丢失组件状态。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.38
Branch: branch04commit description:a1.38(动态组件引例)
tag:a1.38
3.1 example01
以上面的引例为初衷,逐层探究。
3.1.1 example01-1
选项卡切换框子。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.current {
background: yellow;
}
</style>
</head>
<body>
<div id="app">
<button :class="">收邮件</button>
<button :class="">发邮件</button>
<button :class="">垃圾箱</button>
<hr>
<in-box ></in-box>
<post-mail ></post-mail>
<recycle-bin ></recycle-bin>
</div>
<script src="./js/vue.js"></script>
<script>
// 展示当前收件箱
const InBox = {
data() {
return {
items: [
'111111',
'22222222222',
'aaaaaaaa',
'3333333'
]
}
},
template: `
<div>
<ul>
<li v-for="item of items">
<input type="checkbox" />
{
{item}}
</li>
</ul>
</div>
`,
created() {
console.log('InBox:created');
},
destroyed() {
console.log('InBox:destroyed');
}
};
// 展示发邮件的页面
const PostMail = {
template: `
<div>PostMail</div>
`,
created() {
console.log('PostMail:created');
},
destroyed() {
console.log('PostMail:destroyed');
}
};
// 展示当前回收站
const RecycleBin = {
template: `
<div>RecycleBin</div>
`,
created() {
console.log('RecycleBin:created');
},
destroyed() {
console.log('RecycleBin:destroyed');
}
};
let app = new Vue({
el: '#app',
data: {
},
components: {
InBox,
PostMail,
RecycleBin
}
});
</script>
</body>
</html>
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.39
Branch: branch04commit description:a1.39(example01-1——选项卡框子)
tag:a1.39
3.1.2 example01-2
点击按钮,切换页面,暂时不用路由,不做那么复杂。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.current {
background: yellow;
}
</style>
</head>
<body>
<div id="app">
<button @click="goto('InBox')" :class="type=='InBox' ? 'current' : ''">收邮件</button>
<button @click="goto('PostMail')" :class="type=='PostMail' ? 'current' : ''">发邮件</button>
<button @click="goto('RecycleBin')" :class="type=='RecycleBin' ? 'current' : ''">垃圾箱</button>
<hr>
<in-box v-if="type=='InBox'"></in-box>
<post-mail v-if="type=='PostMail'"></post-mail>
<recycle-bin v-if="type=='RecycleBin'"></recycle-bin>
</div>
<script src="./js/vue.js"></script>
<script>
// 展示当前收件箱
const InBox = {
data() {
return {
items: [
'111111',
'22222222222',
'aaaaaaaa',
'3333333'
]
}
},
template: `
<div>
<ul>
<li v-for="item of items">
<input type="checkbox" />
{
{item}}
</li>
</ul>
</div>
`,
created() {
console.log('InBox:created');
},
destroyed() {
console.log('InBox:destroyed');
}
};
// 展示发邮件的页面
const PostMail = {
template: `
<div>PostMail</div>
`,
created() {
console.log('PostMail:created');
},
destroyed() {
console.log('PostMail:destroyed');
}
};
// 展示当前回收站
const RecycleBin = {
template: `
<div>RecycleBin</div>
`,
created() {
console.log('RecycleBin:created');
},
destroyed() {
console.log('RecycleBin:destroyed');
}
};
let app = new Vue({
el: '#app',
data: {
type: 'InBox'
},
components: {
InBox,
PostMail,
RecycleBin
},
methods: {
goto(type) {
this.type = type;
}
}
});
</script>
</body>
</html>
选项卡基本逻辑就有了,但是如果页面上有需求,它会根据某种条件,动态地去展示某个组件(注意这不是页面,而是页面其中一个部分),它可能是多个组件轮番去显示的页面。
这个时候可以用另外一种解决办法。
动态组件,它解决这个问题更为简单,并且以上代码还存在一些问题。
点击“发邮件”,会发现使用v-if
导致,不显示的页面将被销毁,显示的页面将被构建。
如果反复这样做,是非常损耗性能的,这是其一。
其二还有一个问题,切换完毕选项卡后,之前的状态丢失了。大部分情形下,是不希望发生此种情况的。
用v-show
,可以解决该问题,但是处理逻辑会稍许麻烦。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.40
Branch: branch04commit description:a1.40(example01-2——用v-if实现点击按钮,切换组件)
tag:a1.40
3.1.3 example01-3
如果用动态组件可以很好的解决这些问题了。
其内部提供一个component
组件(内置组件,如之前我们学了slot插槽组件),:is=“”
属性,填写组件名称,根据这个值来动态展示组件。
<div id="app">
<button @click="goto('InBox')" :class="type=='InBox' ? 'current' : ''">收邮件</button>
<button @click="goto('PostMail')" :class="type=='PostMail' ? 'current' : ''">发邮件</button>
<button @click="goto('RecycleBin')" :class="type=='RecycleBin' ? 'current' : ''">垃圾箱</button>
<hr>
<component :is="type"></component>
</div>
但是还是会触发销毁和创建!不会保持原有状态。
动态组件其实只关注动态渲染,不会解决这个问题。
解决这个问题,就需要keep-alive 组件了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.41
Branch: branch04commit description:a1.41(example01-3——用动态组件实现点击按钮,切换组件)
tag:a1.41
4. keep-alive 组件
当在这些组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
keep-alive
是一个内置容器组件, 使用 >keep-alive
以后,内部包含的组件将增加 激活
和 失活/冻结
的状态。
它有点类似v-show
组件,但是v-show
只是隐藏了组件,在html
结构(通过浏览器F12
查看elements
)中还是能够找到它的。但是keep-alive
时看不到的,它相当于v-if
和v-show
的结合体。
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
5. 生命周期
使用了 keep-alive
的组件会触发 activated
、deactivated
两个生命周期函数
5.1 activated
keep-alive
组件激活时调用
5.2 deactivated
keep-alive
组件停用时调用
5.3 example02
5.3.1 example02-1
<div id="app">
<button @click="goto('InBox')" :class="type=='InBox' ? 'current' : ''">收邮件</button>
<button @click="goto('PostMail')" :class="type=='PostMail' ? 'current' : ''">发邮件</button>
<button @click="goto('RecycleBin')" :class="type=='RecycleBin' ? 'current' : ''">垃圾箱</button>
<hr>
<keep-alive>
<component :is="type"></component>
</keep-alive>
</div>
keep-alive
和v-show
类似,但是其v-show
仅仅是隐藏,在其浏览器的elemnt
中是可以看到隐藏的部分的,但是keep-alive
是看不到的。它相当于是一个v-show
和v-if
的结合体。
我们看下面的运行,其实组件创建后,就不会再触发组件销毁了,并重新创建了。就相当于它把组件存储在js
当中了,但是把组件对应的页面当中的渲染出来的结果去掉了,即dom树
中没了,但是在js
当中还存在,下次切换,直接去拿构建出来就可以了。
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.42
Branch: branch04commit description:a1.42(example02-1——用动态组件和keep-alive实现点击按钮,切换组件)
tag:a1.42
5.3.2 example02-2
使用了 keep-alive
的组件会触发 activated
、deactivated
两个生命周期函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.current {
background: yellow;
}
</style>
</head>
<body>
<div id="app">
<button @click="goto('InBox')" :class="type=='InBox' ? 'current' : ''">收邮件</button>
<button @click="goto('PostMail')" :class="type=='PostMail' ? 'current' : ''">发邮件</button>
<button @click="goto('RecycleBin')" :class="type=='RecycleBin' ? 'current' : ''">垃圾箱</button>
<hr>
<keep-alive>
<component :is="type"></component>
</keep-alive>
</div>
<script src="./js/vue.js"></script>
<script>
// 展示当前收件箱
const InBox = {
data() {
return {
items: [
'111111',
'22222222222',
'aaaaaaaa',
'3333333'
]
}
},
template: `
<div>
<ul>
<li v-for="item of items">
<input type="checkbox" />
{
{item}}
</li>
</ul>
</div>
`,
created() {
console.log('InBox:created');
},
destroyed() {
console.log('InBox:destroyed');
},
activated() {
console.log('activated')
},
deactivated() {
console.log('deactivated')
}
};
// 展示发邮件的页面
const PostMail = {
template: `
<div>PostMail</div>
`,
created() {
console.log('PostMail:created');
},
destroyed() {
console.log('PostMail:destroyed');
},
activated() {
console.log('PostMail:activated')
},
deactivated() {
console.log('PostMail:deactivated')
}
};
// 展示当前回收站
const RecycleBin = {
template: `
<div>RecycleBin</div>
`,
created() {
console.log('RecycleBin:created');
},
destroyed() {
console.log('RecycleBin:destroyed');
},
activated() {
console.log('RecycleBin:activated')
},
deactivated() {
console.log('RecycleBin:deactivated')
}
};
let app = new Vue({
el: '#app',
data: {
type: 'InBox'
},
components: {
InBox,
PostMail,
RecycleBin
},
methods: {
goto(type) {
this.type = type;
}
}
});
</script>
</body>
</html>
参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.43
Branch: branch04commit description:a1.43(example02-2——用动态组件和keep-alive实现点击按钮,切换组件—打印对应生命周期)
tag:a1.43
(后续待补充)