Vue动态组件、v-if+v-once、v-show的区分使用

2023-05-06 20:03:59 浏览数 (1)

当我们想做出一个toggle的效果,比如点击一下显示文字1,再点击显示文字2,再点击显示文字1....这样交替进行的时候,大家是怎么做的呢?

v-if的使用

用v-if控制切换是一种方法,想用v-show也可以,如下展示v-if方法

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>动态组件与v-once指令
        </title>
        <script src="https://unpkg.com/vue"></script>
    </head>
    <body>
        <div id="app">
            <child-one v-if="comName === 'child-one'"></child-one>
            <child-two v-if="comName === 'child-two'"></child-two>
            <button @click="handleClick">按钮</button>
        </div>
        <script>
            Vue.component('child-one',  {
                template: '<div>child-one</div>'
            })
            Vue.component('child-two',  {
                template: '<div>child-two</div>'
            })
            var vm = new Vue({
                el: '#app',
                data: {
                    comName: 'child-one'
                },
                methods: {
                    handleClick(){
                        this.comName = this.comName === 'child-one' ? 'child-two' : 'child-one';
                    }
                }
            })
        </script>
    </body>
</html>

运行结果

当if条件不成立,组件就会被销毁,条件成立,组件就会被创建,这样就是来回的创建和销毁的过程,有点耗费性能。

component动态组件的使用

接着我们展示动态组件compenent的用法

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>动态组件与v-once指令
        </title>
        <script src="https://unpkg.com/vue"></script>
    </head>
    <body>
        <div id="app">
            <compnent :is="comName"></compnent>
            <!-- <child-one v-if="comName === 'child-one'"></child-one>
            <child-two v-if="comName === 'child-two'"></child-two> -->
            <button @click="handleClick">按钮</button>
        </div>
        <script>
            Vue.component('child-one',  {
                template: '<div>child-one</div>'
            })
            Vue.component('child-two',  {
                template: '<div>child-two</div>'
            })
            var vm = new Vue({
                el: '#app',
                data: {
                    comName: 'child-one'
                },
                methods: {
                    handleClick(){
                        this.comName = this.comName === 'child-one' ? 'child-two' : 'child-one';
                    }
                }
            })
        </script>
    </body>
</html>

运行效果图和上面用v-if一模一样,会根据:is="xxx"的is里面数据的变化自动加载不同的组件,效果和v-if相同,显示另一个组件之前会销毁掉当前组件

v-once指令的使用

这样来回切换不断销毁和创建也是挺耗费性能的,有没有一种办法能把组件缓存起来呢?避免重复销毁创建过程,答案是有的

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>动态组件与v-once指令
        </title>
        <script src="https://unpkg.com/vue"></script>
    </head>
    <body>
        <div id="app">
            <!-- <compnent :is="comName" v-once></compnent> -->
            <child-one v-once v-if="comName === 'child-one'"></child-one>
            <child-two v-once v-if="comName === 'child-two'"></child-two>
            <button @click="handleClick">按钮</button>
        </div>
        <script>
            Vue.component('child-one',  {
                template: '<div>child-one</div>'
            })
            Vue.component('child-two',  {
                template: '<div>child-two</div>'
            })
            var vm = new Vue({
                el: '#app',
                data: {
                    comName: 'child-one'
                },
                methods: {
                    handleClick(){
                        this.comName = this.comName === 'child-one' ? 'child-two' : 'child-one';
                    }
                }
            })
        </script>
    </body>
</html>

运行结果图示和第一个例子一样

记住这里不能在component上面加上v-once,否则不会有切换效果。

因为v-once只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。顾名思义动态组件component的使用,"动态component"被视为"静态",自然就是切换不了的了。就像是优化器在AST打上了标记认为是静态子树(静态节点),比如一个纯文本节点就是静态子树,如果一个节点被标记为静态,那么除了首次渲染生成节点之外,在重新渲染的时候并不会生成新的节点,而是克隆已存在的静态节点。

渲染普通的 HTML 元素在 Vue 中是非常快速的,但有的时候你可能有一个组件,这个组件包含了大量静态内容。在这种情况下,你可以在根元素上添加 v-once 属性以确保这些内容只计算一次然后缓存起来。这就和v-show达到了同样的效果。

官方给出的注意点:不要过度使用这个模式。当你需要渲染大量静态内容时,极少数的情况下它会给你带来便利,除非你非常留意渲染变慢了,不然它完全是没有必要的——再加上它在后期会带来很多困惑。例如,设想另一个开发者并不熟悉 v-once 或漏看了它在模板中,他们可能会花很多个小时去找出模板为什么无法正确更新。

v-if v-once能否取代v-show?

v-if v-once其实在一定的程度上效果和v-show效果是相同的,比如上面的例子,用v-if v-once也是来回切换,v-once把组件缓存起来了,避免了来回创建销毁耗费的性能,而v-show也可以是一样的效果。

我所观察到的区别:

1、DOM结构上的区别

v-show是display的none和block的切换,组件被渲染并一直保留在 DOM 中,而v-if是组件销毁创建的切换,销毁的组件显示为<!---->,加上v-once可以在内存上优化,达到和v-show一样的效果。

2、限制上的区别

v-show不支持 <template> 元素,而v-if支持,这里可以用v-if v-once达到v-show的性能。

举个例子

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>动态组件与v-once指令
        </title>
        <script src="https://unpkg.com/vue"></script>
    </head>
    <body>
        <div id="app">
            <template v-once v-if="comName === 'child-one'">
                <div>这是v-if v-once的child-one</div>
            </template>
            <template v-show="comName === 'child-one'">
                <div>
                    这是v-show的child-one,在template上始终显示,没有判断效果
                </div>
            </template>
            <button @click="handleClick">按钮</button>
        </div>
        <script>
            Vue.component('child-one',  {
                template: '<div>child-one</div>'
            })
            Vue.component('child-two',  {
                template: '<div>child-two</div>'
            })
            var vm = new Vue({
                el: '#app',
                data: {
                    comName: 'child-one'
                },
                methods: {
                    handleClick(){
                        this.comName = this.comName === 'child-one' ? 'child-two' : 'child-one';
                    }
                }
            })
        </script>
    </body>
</html>

运行效果:

我们可以看到不管条件为真还是为假,v-show已经完全失去了判断作用,足以说明在<template>上面是无效的

0 人点赞