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

VUE3之Component(二)

VUE3之Component(二) 非父子组件之间的通信 Provide引用data和响应式 事件总线库mitt的使用 插槽

非父子组件之间的通信

provide和inject的基本使用。

在上层组件中使用provide来定义数据给其子孙使用:

TEXT
import Home from "./Home.vue";
    export default {
        components:{
            Home
        },
        provide:{
            name:"sk",
            age:18
        }
    }
</script>

在子孙组件中使用inject来使用上层组件定义的数据:

TEXT
<template>
    <div>
        HomeContent: {{name}} - {{age}}
    </div>
</template>

<script>
    export default {
        inject:["name","age"]
    }
</script>

Provide引用data和响应式

provide传递data中的数据,应该将provide定义为function,然后return一个对象(就如同data的使用方式一样),这样就能使用this来访问data中的数据:

TEXT
        provide(){
            return {
                name:"sk",
                age:18,
                length:this.names.length
            }
        },
        data(){
            return{
                names:["abc","cba","nba"]
            }
        },

如果想让provide的数据是响应式的,就需要使用vue中的computed方法,该方法返回的是一个ref对象,需要使用value来取出值:

TEXT
// App.vue
        provide(){
            return {
                name:"sk",
                age:18,
                // ref对象,value
                length: computed(()=>this.names.length)
            }
        },

// HomeContent.vue
<template>
    <div>
        HomeContent: {{name}} - {{age}} - {{length.value}}
    </div>
</template>

事件总线库mitt的使用

首先安装mitt:

TEXT
npm install mitt

写一个工具类eventBus.js导出一个emitter实例:

TEXT
import mitt from "mitt";
const emitter = mitt();

export default emitter;

通过键值对的方式向emitter注册一个消息:

TEXT
        methods: {
            btnClick(){
                console.log("about按钮点击");
                emitter.emit("why",{name:"sk",age:18});
                emitter.emit("kobe",{name:"kobe",age:30});
            }
        },

在一个组件的created生命周期中注册监听一个消息:

TEXT
        created() {
            emitter.on("why",(info)=>{
                console.log("why",info);
            });
            emitter.on("kobe",(info)=>{
                console.log("kobe",info);
            });

            // *表示所有的事件
            emitter.on("*",(type,info)=>{
                console.log("* listener",type,info);
            })
            
            // 取消emitter的所有监听
            // emitter.all.clear();

            // 定义一个函数
            // function onFoo(){}
            // emitter.on("foo",onFoo);
            // emitter.off("foo",onFoo);
        },

插槽

基本使用:

使用slot标签在子组件中定义一个插槽:

TEXT
<template>
    <div>
        <h2>组件的开始</h2>
        <slot></slot>
        <h2>组建的结束</h2>
    </div>
</template>

在父组件中使用这个子组件,并在子组件标签中放入插槽元素:

TEXT
<template>
    <div>
        <my-slot-cpn>
            <button>我是按钮</button>
            <my-button/>
        </my-slot-cpn>
    </div>
</template>

在slot标签中添加元素可以指定插槽的默认元素:

TEXT
<template>
    <div>
        <h2>组件的开始</h2>
        <slot>
            <i>我是默认的i元素</i>
        </slot>
        <h2>组建的结束</h2>
    </div>
</template>

具名插槽

通过给slot标签增加name属性来设置具名插槽:

TEXT
<template>
    <div class="nav-bar">
        <div class="left">
            <slot name="left"></slot>
        </div>
        <div class="center">
            <slot name="center"></slot>
        </div>
        <div class="right">
            <slot name="right"></slot>
        </div>
    </div>
</template>

使用过程中通过template的v-slot属性来指定使用某一个插槽:

TEXT
<template>
    <div>
        <nav-bar :name="name">
            <template v-slot:left>
                <button>左边的按钮</button>
            </template>
            <template v-slot:center>
                <h2>我是标题</h2>
            </template>
            <template v-slot:right>
                <button>右边的按钮</button>
            </template>
        </nav-bar>
    </div>
</template>

动态命名插槽:可以结合props来动态指定一个插槽的名称:

TEXT
// NavBar.vue
<template>
    <div class="nav-bar">
        <div class="addition">
            <slot :name="name"></slot>
        </div>
    </div>
</template>

<script>
    export default {
        props:{
            name:String
        }
    }
</script>

// App.vue
<template>
    <div>
        <nav-bar :name="name">
            <template v-slot:[name]>
                <button>动态名称</button>
            </template>
        </nav-bar>
    </div>
</template>

<script>
import NavBar from "./NavBar.vue";
    export default {
        components:{
            NavBar
        },
        data() {
            return {
                name:"sk"
            }
        },
    }
</script>

插槽有默认的名称default。

v-slot:的缩写是#

作用域插槽

作用域的概念是:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的;

在slot标签中增加属性,此属性的值可以在父组件中通过template的v-slot获取到一个实例(如slotProps),并通过slotProps来访问slot的属性:

TEXT
// ShowNames.vue
<template>
    <div>
        <template v-for="(item,index) in names">
            <slot :item="item" :index="index"></slot>
        </template>
    </div>
</template>

<script>
    export default {
        props:{
            names:{
                type: Array,
                default:()=>[]
            }
        }
    }
</script>

// App.vue
<template>
    <div>
        <show-names :names="names">
            <template v-slot="slotProps">
                <button>{{slotProps.item}}-{{slotProps.index}}</button>
            </template>
        </show-names>
    </div>
</template>

如果是具名插槽,在父组件的template中可以指定某一个具体的名称:

TEXT
<template v-slot:left="slotProps">

独占默认插槽的缩写:如果只有一个插槽,并且是非具名的,可以有以下缩写

TEXT
        <show-names :names="names" v-slot="slotProps">
                <button>{{slotProps.item}}-{{slotProps.index}}</button>
        </show-names>

通过 RSS 订阅更新 →