Blog of CodeJELLOW’S ENGINEERING NOTES
← 返回文章

VUE3之Component(三)

VUE3之Component(三) 动态组件的基本使用 动态组件的传递参数 keep-alive 异步组件 suspense和异步组件结合使用 $refs $parent和$root 生命周期 组件的v-model

动态组件的基本使用

根据一个属性currentTab来决定当前显示的组件,可以使用template的v-if来完成:

TEXT
        <template v-if="currentTab==='home'">
            <div>
                <home/>
            </div>
        </template>
        <template v-else-if="currentTab==='about'">
            <div>
                <about/>
            </div>
        </template>
        <template v-else>
            <div>
                <category/>
            </div>
        </template>

也可以使用动态组件完成。动态组件是component标签,有一个属性is,用于决定当前动态显示的组件:

TEXT
<component :is="currentTab"></component>

is属性后面的内容必须是:is属性需要的内容:component函数全局注册的组件;或当前app的components局部注册的组件。

动态组件的传递参数

动态组件的传递参数和普通组件类似,同样是在组件内部定义props表明需要使用的参数,定义emits表明需要触发的事件;在动态组件中用同样的方式传入参数与回调的方法。

TEXT
// Home.vue
<template>
    <div @click="divClick">
        Home 组件 {{name}} - {{age}}
    </div>
</template>

<script>
    export default {
        props:{
            name:{
                type:String,
                default:""
            },
            age:{
                type:Number,
                default:0
            }
        },
        emits:["pageClick"],
        methods: {
            divClick(){
                this.$emit("pageClick");
            }
        },
    }
</script>
TEXT
// App.vue
        <component :is="currentTab"
            name="sk"
            :age="18"
            @pageClick="pageClick"></component>

keep-alive

在动态组件中切换组件时会将之前的组件销毁,切换回来时组件会被重新创建,因此该组件的状态和数据就会丢失。

在组件外嵌套一个keep-alive标签可以防止组件被摧毁:

TEXT
        <keep-alive>
            <component :is="currentTab"
                name="sk"
                :age="18"
                @pageClick="pageClick"></component>
        </keep-alive>

keep-alive有三个属性可以用:include是指哪些组件需要被keep alive,exclude是指不需要的组件,max指明缓存组件的最大数量,替换策略是LRU。

以include为例:

TEXT
        <keep-alive include="home,about">
            <component :is="currentTab"
                name="sk"
                :age="18"
                @pageClick="pageClick"></component>
        </keep-alive>

include后面可以跟组件名,该组件名必须得在组件中用name属性声明:

TEXT
// Home.vue
<script>
    export default {
        name: "home",        
    }
</script>

也可以用v-bind:+正则表达式或数组。

异步组件

webpack的分包方式:在引用一个自己写的文件时,使用import函数而非直接使用import关键字,可以使得webpack打包时将引用的文件单独打包为一个文件:

TEXT
// 通过import函数导入的模块,后续webpack对其打包时会进行分包的操作
import("./12_异步组件的使用/utils/math").then((res)=>{
    console.log(res.sum(20,13));
})
图片暂不可用。

定义异步组件:

使用vue3中的defineAsyncComponent方法,

TEXT
import {defineAsyncComponent} from "vue";

该方法的返回值是一个组件,使用方式有两种,一种是传入一个工厂方法:

TEXT
// 传入工厂函数
// const AsyncCategory = defineAsyncComponent(()=>import("./AsyncCategory.vue"));;

还有一种是传入一个对象,在对象中可以定义其它的属性:

TEXT
const AsyncCategory = defineAsyncComponent({
    loader: ()=>import("./AsyncCategory.vue"),
    // 加载之前的占位组件
    loadingComponent: Loading,
    // 加载错误的组件
    // errorComponent:
    // 延迟显示:在显示loadingComponent组件之前,等待多长时间
    delay: 2000,
    /**
     * err:错误信息
     * retry:函数,调用retry尝试重新加载
     * attempts:记录尝试次数
     */
    onError: function(err,retry,attempts){

    }
})

suspense和异步组件结合使用

使用suspense标签,并在其中的template中指定default和fallback,这样也能达到在default中的元素加载失败后去加载fallback中元素的效果。

TEXT
<template>
    <div>
        <suspense>
            <template #default>
                <async-category></async-category>
            </template>
            <template #fallback>
                <loading></loading>
            </template>
        </suspense>
    </div>
</template>

<script>
import {defineAsyncComponent} from "vue";


import Loading from "./Loading.vue";eAsyncComponent(()=>import("./AsyncCategory.vue"));
    export default {
        components:{
            AsyncCategory,
            Loading
        }
        
    }
</script>

$refs

对元素或者组件添加refs属性,可以获取其实例

获取元素时直接获取到的是该元素的实例,并可以使用其Dom方法。

TEXT
<template>
    <div>
        <!-- 绑定到一个元素上 -->
        <h2 ref="title">哈哈哈</h2>
        <button @click="btnClick">获取元素</button>

        <!-- 绑定到一个组件实例上 -->
        <nav-bar ref="navBar"></nav-bar>
    </div>
</template>

<script>
import NavBar from "./Nav.vue";
    export default {
        components:{
            NavBar
        },
        methods: {
            btnClick(){
                console.log(this.$refs.title);
                console.log(this.$refs.navBar.message);
                this.$refs.navBar.sayHello();
            }
        },
    }
</script>

获取组件时,得到的是一个代理,通过代理可以获取其定义的data、methods等属性

TEXT
<template>
    <div>
        <h2>NavBar</h2>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                message:"我是nav中的message"
            }
        },
        methods: {
            sayHello(){
                console.log("hello navBar");
            }
        },
    }
</script>

$parent和$root

在子组件中可以通过$parent和$root来获取父组件和根组件。

TEXT
            getParentAndRoot(){
                console.log(this.$parent);
                console.log(this.$root);
            }

生命周期

在某一个生命周期的阶段时回调一个方法:

TEXT
// App.vue
<template>
    <div>
        <button @click="isShow=!isShow">切换</button>
        <template v-if="isShow">
            <home></home>
        </template>
    </div>
</template>

<script>
import Home from "./Home.vue";
    export default {
        data() {
            return {
                isShow:true
            }
        },
        components:{
            Home
        }
    }
</script>
TEXT
// Home.vue
<template>
    <div>
        <h2 ref="title">{{message}}</h2>
        <button @click="changeMessage">修改message</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                message:"hello home"
            }
        },
        methods: {
            changeMessage(){
                this.message="hello change"
            }
        },
        beforeCreate() {
            console.log("beforeCreate");
        },
        created() {
            console.log("created");
        },
        beforeMount() {
            console.log("beforeMount");
        },
        mounted() {
            console.log("mounted");
        },
        beforeUnmount() {
            console.log("beforeUnmount");
        },
        unmounted() {
            console.log("unmounted");
        },
        beforeUpdate() {
            console.log(this.$refs.title.innerHTML);
            console.log("beforeUpdate");
        },
        updated() {
            console.log(this.$refs.title.innerHTML);
            console.log("updated");
        },
    }
</script>

对于keep-alive的缓存组件,可以使用activated和deactivated来回调激活与取消的状态:

TEXT
<script>
    export default {
        name:"about",
        data() {
            return {
                counter:0
            }
        },
        created() {
            console.log("about created");
        },
        unmounted() {
            console.log("about unmounted");
        },
        activated() {
            console.log("about activated");
        },
        deactivated() {
            console.log("about deactivated");
        },
    }
</script>
图片暂不可用。

组件的v-model

基本使用:一个组件的v-model实际上是添加了两个属性:modelValue和@update:model-value:

TEXT
<my-input :modelValue="message" @update:model-value="message=$event"></my-input>

在组件中就需要有对应的props和emits:

TEXT
<template>
    <div>
        <input type="text" :value="modelValue" @input="btnClick">
    </div>
</template>

<script>
    export default {
        props:{
            modelValue: String
        },
        emits:["update:modelValue"],
        methods: {
            btnClick(event){
                this.$emit("update:modelValue",event.target.value);
            }
        },
    }
</script>

也可以通过计算属性来实现:

TEXT
<template>
    <div>
        <input type="text" v-model="value">
    </div>
</template>

<script>
    export default {
        props:{
            modelValue: String
        },
        emits:["update:modelValue"],
        computed:{
            value:{
                set(value){
                    this.$emit("update:modelValue",value);
                },
                get(){
                    return this.modelValue;
                }
            }
        },
        },
    }
</script>

绑定多个属性:可以在v-model后面用:增加新的属性,对应的在组件中也会有props和emits,以v-model:title为例:

TEXT
<my-input-2 v-model="message" v-model:title="title"></my-input-2>

props中需要的名称就是title,emits中需要的事件叫@update:title:

TEXT
<template>
    <div>
        <input type="text" v-model="value">
        <input type="text" v-model="titlec">
    </div>
</template>

<script>
    export default {
        props:{
            modelValue: String,
            title:String
        },
        emits:["update:modelValue","update:title"],
        computed:{
            value:{
                set(value){
                    this.$emit("update:modelValue",value);
                },
                get(){
                    return this.modelValue;
                }
            },
            titlec:{
                set(value){
                    this.$emit("update:title",value);
                },
                get(){
                    return this.title;
                }
            }
        },
    }
</script>
通过 RSS 订阅更新 →