Vue 【组件】组件注册、组件生命周期、动态组件、keep-alive

目录:

1. 组件注册

2. 组件生命周期

3. 动态组件

4. keep-alive 组件

 

一. 组件注册

全局注册

全局注册的组件可以在任何地方使用。

使用全局注册注册的组件会有一个问题,当业务进行工程最终打包的过程中会将所有的组件都打包进去,如果后续有组件被弃用,这有可能会造成一些不可控的体积的变化。

例子:https://www.cnblogs.com/xiaoxuStudy/p/13206849.html

 

局部注册

局部注册的组件只能在当前组件中使用。在单文件组件中,可以局部注册一个组件供当前组件使用。

步骤: 首先 import 组件,然后通过 components 导入,然后就可以在模板中使用该组件了。

举个例子:

在 App.vue 中局部注册组件 LifeCycleA.vue。

LifeCycleA.vue :

<template>
    <div>
        我来自LifeCycleA.vue
    </div>
</template>
LifeCycleA.vue

App.vue :

<template>
    <div>
        我来自App.vue
        <l-a />
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";

export default {
    components: {
        "l-a": LifeCycleA
    }
}
</script>
App.vue

页面表现:

 

Q: 平时在开发组件的过程当中,如果组件都是局部引入的,那么可能会经常去引入组件,这样比较浪费时间,该怎么办?

A: 如果使用 webpack,使用 Vue CLI 来创建工程项目的话,那么可以使用 require.context 这个方法来批量地导入组件文件,然后再用 Vue.component 来注册组件,这样做可以简化手动管理组件动作,但是这样仍然无法解决可能导致项目体积不可控的问题。

 

二. 组件生命周期

  当创建一个 Vue 实例的时候,Vue 首先会初始化事件以及生命周期钩子函数,这时候会抛出一个 beforeCreate 钩子函数供我们使用,在 beforeCreate 的时候,不允许访问页面上的 data、method 以及相应的 dom节点。初始化注入和校验完毕的时候,会有一个 created 钩子函数,在 created 钩子函数里面可以访问 data、method 以及响应式 computed 的数据,完成后,Vue 进行模板解析与渲染。模板编译完成后,会抛出 beforeMount 钩子函数,beforeMount 是指模板编译好但是模板还没有根据响应式数据进行渲染。渲染完毕后,抛出 mounted 钩子函数。mounted 执行完毕后,代表组件已经初始化完毕,当去响应式地修改数据时又进入了一个新的循环,当 data(数据)改变了,会抛出 beforeUpdate 钩子函数。beforeUpdate 结束后,开始根据当前的数据去进行 diffpatch 的过程,最后抛出 updated 钩子函数。当组件进行销毁时(比如调用vm.$destroy),或者通过 v-if 切换组件时,在组件卸载前会抛出 beforeDestroy 钩子函数,这时会解除绑定、销毁子组件以及事件监听器。销毁完毕后,抛出 destroyed 钩子函数供我们使用。

Vue 官网上生命周期的API:
https://cn.vuejs.org/v2/api/#选项-生命周期钩子

Vue 官网上的生命周期图示:

https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA

 

 

下面通过代码了解生命周期。App.vue 是父组件,LifeCycleA.vue是子组件。

 App.vue : 

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <l-a v-if="ab"></l-a>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";

export default {
    data(){
        return{
            ab: true    
        };
    },
    components: {
        "l-a": LifeCycleA
    }
}
</script>
App.vue

LifeCycleA.vue :

<template>
    <div>
        LifeCycle A{{ test }}
        <input type="number" v-model="test" ref="input" />
    </div>
</template>

<script>
export default {
    data(){
        return{
            test: 1
        };
    },
    computed: {
        bontest(){
            return this.test * 2;
        }
    },
    methods: {
        getOne(){}
    },
    beforeCreate(){
        console.group("=== 初始化 ===");
        console.log("=== beforeCreate ===");

        console.log(` [data:test] ${this.test} `);
        console.log(` [compute:bontest] ${this.bontest} `);
        console.log(` [method:getOne] ${this.getOne} `);
        console.log(` [ref:input] ${this.$refs.input} `);
    },
    created(){
        console.log("=== created ===");

        console.log(` [data:test] ${this.test} `);
        console.log(` [compute:bontest] ${this.bontest} `);
        console.log(` [method:getOne] ${this.getOne} `);
        console.log(` [ref:input] ${this.$refs.input} `);

        console.groupEnd();
    },
    beforeMount(){
        console.group("=== 挂载 ===");
        console.log("=== beforeMount ===");
        console.log(` [ref:input] ${this.$refs.input} `);
    },
    mounted(){
        console.log("=== mounted ===");
        console.log(` [ref:input] ${this.$refs.input} `);

        console.groupEnd();
    },
    beforeUpdate(){
        console.group("=== 模板更新 ===");
        console.log("=== boforeUpdate ===");

        console.log(` test: ${this.test} `);
    },
    updated(){
        console.log("=== updated ===");

        console.log(` test: ${this.test} `);

        console.groupEnd();
    },
    activated(){
        console.group("=== keepAlive 激活 ===");
        console.log("=== activated ===");
    },
    deactivated(){
        console.log("=== deactivated ===");
        console.groupEnd();
    },
    beforeDestroy(){
        console.group("=== 销毁 ===");
        console.log("=== beforeDestroy ===");
    },
    destroyed(){
        console.log("=== destroyed ===");
        console.groupEnd();
    }
}
</script>
LifeCycle.vue

页面表现:

 说明:

  可以看到,在 beforeCreate 阶段,不允许访问页面上的 date、method、computed 以及相应的 dom 节点,访问这些数据返回的都是 undefined。在 created 阶段,响应式数据都被添加进来了,date、method、computed 都可以访问到一般都在 created 钩子函数里做数据的初始化请求。在 beforeMount 阶段,不允许访问 dom 节点,可以看到访问 $ref 返回 undefined。在 mounted 阶段,可以看到可以访问 input 的 dom 节点了。

在页面上修改值后,进行了模板更新:

说明:

  在 beforeUpdate 或者 updated 阶段去访问 test 的值得到的都是更新后的值。所以,如果需要判断数据更新前后的值,就需要使用到 watch 函数在模板更新里进行操作。

 点击“切换AB”按钮之后进行了销毁:

 

三. 动态组件

  例子 :  点击按钮切换组件 

  实现点击按钮,切换显示组件。

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <l-a v-if="ab"></l-a>
        <l-for v-if="!ab"></l-for>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";
import FOR from "./components/For"

export default {
    data(){
        return{
            ab: true    
        };
    },
    components: {
        "l-a": LifeCycleA,
        "l-for": FOR
    }
}
</script>
App.vue
<template>
    <div>
        LifeCycle A{{ test }}
        <input type="number" v-model="test" ref="input" />
    </div>
</template>
LifeCycleA.vue
<template>
    <div>
        <div v-for="(p, index) in classmates" :key="p.id">
            {{ `${index}.${p.name}` }}
        </div>
    </div>
</template>

<script>
export default {
    data(){
        return{
            classmates: [
                {id: 1, name: "许一"},
                {id: 2, name: "许二"},
                {id: 3, name: "许三"},
            ]
        };
    }
}
</script>
For.vue

App.vue:

页面表现:

  点击“切换AB”按钮,会切换组件显示 LifeCycleA 跟 FOR 组件。

 

  例子 : 点击按钮切换组件的同时切换文字 title  

  在上面代码的基础上, 添加一些文字,切换组件的时候文字也切换,这里切换 title。

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <div class="title" v-if="ab">title1</div>
        <div class="title" v-if="!ab">title2</div>
        <l-a v-if="ab"></l-a>
        <l-for v-if="!ab"></l-for>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";
import FOR from "./components/For"

export default {
    data(){
        return{
            ab: true    
        };
    },
    components: {
        "l-a": LifeCycleA,
        "l-for": FOR
    }
}
</script>
App.vue

页面表现:

 

  例子:更好地实现切换 title  

  很明显,这个结构是一样的,可以采用 computed 去复用这个结构。(computed相关笔记:https://www.cnblogs.com/xiaoxuStudy/p/13230664.html#computed)

  代码说明:ab 初始值是 true,点击按钮实现 ab 的值在 true 与 false 之间切换。文本插值 title,使用 computed 判断 title 的值,如果 ab 的值是 true,则 title 的值为 title1,如果 ab 的值为 false,则 title 的值为 title2。

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <div class="title">{{ title }}</div>
        <l-a v-if="ab"></l-a>
        <l-for v-if="!ab"></l-for>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";
import FOR from "./components/For"

export default {
    data(){
        return{
            ab: true    
        };
    },
    computed: {
        title(){
            return this.ab ? "title1" : "title2";
        }
    },
    components: {
        "l-a": LifeCycleA,
        "l-for": FOR
    }
}
</script>
App.vue

页面效果同上

 

  例子 :更好地实现切换组件  

  很明显,这个结构是相似的。可以采用 computed 去复用这个结构,可以使用动态组件引入组件,同时也不需要注册组件了。

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <div class="title">{{ title }}</div>
        <component :is="currentComponent"></component>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";
import FOR from "./components/For"

export default {
    data(){
        return{
            ab: true    
        };
    },
    computed: {
        title(){
            return this.ab ? "title1" : "title2";
        },
        currentComponent() {
            return this.ab ? LifeCycleA : FOR;
        }
    }
}
</script>
App.vue

页面效果同上

 

动态组件格式

<component v-bind:is="currentComponent"></component>

  使用 vue 中的动态组件 component,用 v-bind:is 来确定当前的组件是什么,其中的 currentComponent 是自定义的,可以是已经注册的组件的名字,也可以是组件的构造函数。

  动态组件可以帮助我们更大程度地复用 JS 代码,而不是将业务逻辑都放在模板中,放在模板中是不利于我们进行单元测试的。

 

 

四. keep-alive 组件

上面的例子中,如果页面上进行操作后切换组件,再切换回来,组件会重新渲染,像下面这样:

 

如果不想切换组件后内容丢失,可以使用 keep-alive 去缓存。

  例子  

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <div class="title">{{ title }}</div>
        <keep-alive>
            <component :is="currentComponent"></component>
        </keep-alive>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";
import FOR from "./components/For"

export default {
    data(){
        return{
            ab: true    
        };
    },
    computed: {
        title(){
            return this.ab ? "title1" : "title2";
        },
        currentComponent() {
            return this.ab ? LifeCycleA : FOR;
        }
    }
}
</script>
App.vue

页面表现:

说明:

  可以使用 keep-alive 组件去缓存 App.vue 的子组件的一个实例,当组件再次出现在页面中时,不需再次创建组件,而是直接通过之前缓存的组件实例并且通过组件上的 vm.$el 获得先前 DOM 元素,然后用该 DOM 元素直接插入到页面中去,从而提升组件和页面更新的效率。

 

keep-alive 提供的 Props 跟 子组件 Life Hook

Props:

子组件 Life Hook:

 

  例子  

在上面例子的基础上,对 LifeCycleA.vue 进行改动,使得方便观察生命周期函数的调用。

<template>
    <div>
        LifeCycle
        <button @click="ab=!ab">切换AB</button>
        <div class="title">{{ title }}</div>
        <keep-alive>
            <component :is="currentComponent"></component>
        </keep-alive>
    </div>
</template>

<script>
import LifeCycleA from "./components/LifeCycleA";
import FOR from "./components/For"

export default {
    data(){
        return{
            ab: true    
        };
    },
    computed: {
        title(){
            return this.ab ? "title1" : "title2";
        },
        currentComponent() {
            return this.ab ? LifeCycleA : FOR;
        }
    }
}
</script>
父组件 App.vue
<template>
    <div>
        <div v-for="(p, index) in classmates" :key="p.id">
            {{ `${index}.${p.name}` }}
        </div>
    </div>
</template>

<script>
export default {
    data(){
        return{
            classmates: [
                {id: 1, name: "许一"},
                {id: 2, name: "许二"},
                {id: 3, name: "许三"},
            ]
        };
    }
}
</script>
子组件 For.vue
<template>
    <div>
        LifeCycle A{{ test }}
        <input type="number" v-model="test" ref="input" />
    </div>
</template>

<script>
export default {
    data(){
        return{
            test: 1
        };
    },
    computed: {
        bontest(){
            return this.test * 2;
        }
    },
    methods: {
        getOne(){}
    },
    beforeCreate(){
        console.group("=== 初始化 ===");
        console.log("=== beforeCreate ===");

        console.log(` [data:test] ${this.test} `);
        console.log(` [compute:bontest] ${this.bontest} `);
        console.log(` [method:getOne] ${this.getOne} `);
        console.log(` [ref:input] ${this.$refs.input} `);
    },
    created(){
        console.log("=== created ===");

        console.log(` [data:test] ${this.test} `);
        console.log(` [compute:bontest] ${this.bontest} `);
        console.log(` [method:getOne] ${this.getOne} `);
        console.log(` [ref:input] ${this.$refs.input} `);

        console.groupEnd();
    },
    beforeMount(){
        console.group("=== 挂载 ===");
        console.log("=== beforeMount ===");
        console.log(` [ref:input] ${this.$refs.input} `);
    },
    mounted(){
        console.log("=== mounted ===");
        console.log(` [ref:input] ${this.$refs.input} `);

        console.groupEnd();
    },
    beforeUpdate(){
        console.group("=== 模板更新 ===");
        console.log("=== boforeUpdate ===");

        console.log(` test: ${this.test} `);
    },
    updated(){
        console.log("=== updated ===");

        console.log(` test: ${this.test} `);

        console.groupEnd();
    },
    activated(){
        console.group("=== keepAlive 激活 ===");
        console.log("=== activated ===");
    },
    deactivated(){
        console.log("=== deactivated ===");
        console.groupEnd();
    },
    beforeDestroy(){
        console.group("=== 销毁 ===");
        console.log("=== beforeDestroy ===");
    },
    destroyed(){
        console.log("=== destroyed ===");
        console.groupEnd();
    }
}
</script>
子组件 LifeCycleA.vue

LifeCycleA.vue:

页面表现:

点击按钮切换组件后

原文地址:https://www.cnblogs.com/xiaoxuStudy/p/13200544.html