Vue 0基础学习路线(10)—— 图解深度详述Vue动态组件和keep-alive组件、及其生命周期及详细案例(附详细案例代码解析过程及版本迭代过程)

1. 重点提炼

  • 动态组件
    • <component></component>
    • <keep-alive></keep-alive>
    • 生命周期
      • 冻结
      • 激活

2. 动态组件

有的时候,我们需要在多个不同的组件之间进行切换(类似选项卡的效果)。虽然我们可以通过 v-if 来处理,但是会比较麻烦,vue 提供了一个更方便的方式来处理这种情况。

3. component 组件

componentvue 内置的一个组件,它提供一个 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: branch04

commit 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: branch04

commit 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: branch04

commit 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: branch04

commit 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-ifv-show的结合体。

<keep-alive>
  <component :is="currentComponent"></component>
</keep-alive>

5. 生命周期

使用了 keep-alive 的组件会触发 activateddeactivated 两个生命周期函数

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-alivev-show类似,但是其v-show仅仅是隐藏,在其浏览器的elemnt中是可以看到隐藏的部分的,但是keep-alive是看不到的。它相当于是一个v-showv-if的结合体。

我们看下面的运行,其实组件创建后,就不会再触发组件销毁了,并重新创建了。就相当于它把组件存储在js当中了,但是把组件对应的页面当中的渲染出来的结果去掉了,即dom树中没了,但是在js当中还存在,下次切换,直接去拿构建出来就可以了。

在这里插入图片描述

参考:https://https://github.com/6xiaoDi/blog-vue-Novice/tree/a1.42
Branch: branch04

commit description:a1.42(example02-1——用动态组件和keep-alive实现点击按钮,切换组件)

tag:a1.42

5.3.2 example02-2

使用了 keep-alive 的组件会触发 activateddeactivated 两个生命周期函数。

<!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: branch04

commit description:a1.43(example02-2——用动态组件和keep-alive实现点击按钮,切换组件—打印对应生命周期)

tag:a1.43



(后续待补充)

猜你喜欢

转载自blog.csdn.net/u013946061/article/details/107717169