vue之定义局部组件、组件编写方式与Vue实例的区别、组件通信、事件总线、动态组件及slot插槽

1、定义局部组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <global_f></global_f>
    <div @click="clickHandler">我是数据</div>

    <child1></child1>
    <child2></child2>
</div>

<script>

    // 定义全局组件
    Vue.component('global_f', {
        template: `
        <div>
            <div @click="showInfo">我是全局组件</div>
            <div v-if="isShow">
                <div>显示或消失</div>
                <div>名字是: {{name}}</div>
                <div>年龄是: {{age}}</div>
            </div>
        </div>
       `,
        data() {
            return {
                name: 'allen',
                age: 18,
                isShow: true,
            }
        },
        methods: {
            showInfo() {
                console.log('全局组件的方法: ', this.name, this.age);
                this.isShow = !this.isShow;
            },
        }
    });

    var vm = new Vue({
        el: '#box',
        data: {},
        methods: {
            clickHandler() {
                console.log('我被点击了');
            }
        },
        // 定义子组件
        components: {
            child1: {
                template: `
                <div @click="clickC1">我是子组件1</div>
              `,
                methods: {
                    clickC1() {
                        console.log('child1被点击了');
                    }
                }
            },
            child2: {
                template: `
                <div @click="clickC2">我是子组件2</div>
              `,
                methods: {
                    clickC2() {
                        console.log('child2被点击了');
                    }
                }
            }
        },
    })
</script>

</body>
</html>

2、组件编写方式与Vue实例的区别

1、自定义组件需要有一个root element,一般包裹在一个div中,跟vue实例一样
2、父子组件的data是无法共享的
3、组件可以有data,methods,computed...,但是data必须是一个函数
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <navbar></navbar>
    {{xxx}}
</div>

<script>
    // 定义全局组件
    Vue.component('navbar', {
        // 全局组件中定义子组件child
        template: `
            <div>
                <button @click="handleClick">返回</button>
                我是NavBar: {{x}}
                <button style="background: red">主页</button>
                <br>
                <child></child>
            </div>
        `,
        methods: {
            handleClick() {
                console.log('nav nav');
            },
        },
        components: {
            child: {
                template: `
                    <button @click="clickHandle">子组件</button>
                `,
                methods: {
                    clickHandle() {
                        console.log('子组件被点击了');
                    }
                },
            }
        },
        data() {
            return {
                x: 'hello world',
            }
        },
    });

    var vm = new Vue({
        el: '#box',
        data: {
            xxx: 18,
        },
        methods: {},
    })
</script>

</body>
</html>

3、组件通信

1、父子组件传值 (props down, events up)
2、父传子之属性验证props:{name:Number}Number,String,Boolean,Array,Object,Function,null(不限制类型)
3、事件机制a.使用 $on(eventName) 监听事件b.使用 $emit(eventName) 触发事件
4、Ref<input ref="mytext"/>  this.$refs.mytext
5、事件总线var bus = new Vue();* mounted生命周期中进行监听
3.1、父传子通信
3.1.1、具体步骤
(1) 父子通信
(2) 在全局组件上自定义属性
	<info myname="allen" mybtn="btn1"></info>
(3) 在组件中获取
	props: ['myname', 'mybtn']  # myname=allen   mybtn=btn1     
(4) 注意区分以下赋值方式
     <info myname="allen" mybtn="btn1"></info>
     <info :myname="'jack'" mybtn="btn2"></info>
     <info :myname="name" :mybtn="btn"></info>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <!--myname和mybtn是自定义属性-->
    <info myname="allen" mybtn="btn1"></info>
    <info :myname="'jack'" mybtn="btn2"></info>
    <info :myname="name" :mybtn="btn"></info>
</div>

<script>
    // 定义全局组件
    Vue.component('info', {
        template: `
            <div>
                <div>我要传递的数据: {{myname}}</div>
                <button style="background: red">{{mybtn}}</button>
            </div>
        `,
        props: ['myname', 'mybtn'],
    });

    var vm = new Vue({
        el: '#box',
        data: {
            name: 'tom',
            btn: 'btn3',
        },
        methods: {},
    })
</script>

</body>
</html>
3.1.2、属性验证
1、限制父传子的变量类型
	  props: {
            myname:String,
            isshow:Boolean
        }
2、父传子时注意以下的区别
    <info myname="allen" :isshow=isshow></info>
    <info myname="jack" :isshow=true></info>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <!--myname和mybtn是自定义属性-->
    <info myname="allen" :isshow=isshow></info>
    <info myname="jack" :isshow=true></info>
</div>

<script>
    // 定义全局组件
    Vue.component('info', {
        template: `
            <div>
                <div>我要传递的数据: {{myname}}</div>
                <div>我是info: {{isshow}}</div>
            </div>
        `,
        props: {
            myname: String,
            isshow: Boolean,
        },
    });

    var vm = new Vue({
        el: '#box',
        data: {
            name: 'allen',
            isshow: false,
        },
        methods: {},
    })
</script>

</body>
</html>
3.2、子传父通信
3.2.1、具体步骤
通过事件实现: 点击一下子组件,就会触发父组件某个函数的执行
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <navbar @myevent="clickHandler"></navbar>
    <div v-model="mydata">子组件传递过来的数据: {{mydata}}</div>
</div>

<script>
    // 定义全局组件
    Vue.component('navbar', {
        template: `
        <div>
            <button @click="navHandler">点击触发父组件的某个函数执行</button>
        </div>
        `,
        data() {
            return {
                name: 'allen',
            }
        },
        methods: {
            navHandler() {
                console.log('我是nav函数我执行了');
                this.$emit('myevent', this.name);
            },
        }
    });

    var vm = new Vue({
        el: '#box',
        data: {
            mydata: '',
        },
        methods: {
            clickHandler(a) {
                console.log(a);
                this.mydata = a;
            },
        },
    })
</script>

</body>
</html>
3.2.2、小案例

子组件有一个按钮,有一个输入框,当输入完内容,点击按钮,数据在父组件中展示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <navbar @myevent="getData"></navbar>
    <hr>
    <div>展示得到的数据: {{mytext}} {{mydata}}</div>
</div>

<script>
    // 定义全局组件
    Vue.component('navbar', {
        template: `
            <div>
                <div>请输入数据: <input type="text" v-model="mytext"></div>
                <button @click="clickHandler">点我在父组件中展示输入内容</button>
            </div>
        `,
        data() {
            return {
                mytext: '',
                mydata: '这个数据默认的',
            }
        },
        methods: {
            clickHandler() {
                this.$emit('myevent', this.mytext, this.mydata)
            }
        }
    });

    var vm = new Vue({
        el: '#box',
        data: {
            mytext: '',
            mydata: '',
        },
        methods: {
            getData(x, y) {
                this.mytext = x;
                this.mydata = y;
            }
        },
    })
</script>

</body>
</html>
3.3、ref属性

也可以实现组件间通信,子向父,父向子传递数据都可以使用

1、ref放在标签上,拿到的是原生节点
2、ref放在组件上,拿到的是组件对象
通过这种方式实现子传父(this.$refs.mychild.text)
通过这种方式实现父传子(调用子组件方法传参数)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <child ref="mychild"></child>
    <hr>
    <button @click="handleButton">点我</button>
</div>


<script>
    // 定义全局组件
    Vue.component('child', {
        template: `
        <div>
        <input type="text" v-model="mytext">
        <hr>
        我是子组件的input
        </div>
        `,
        data() {
            return {
                mytext: '',
            }
        },
        methods: {
            add(x) {
                console.log('我是子组件的add方法');
                console.log(x);
                return '返回数据';
            }
        }
    });

    var vm = new Vue({
        el: '#box',
        data: {
            name: 'allen',
        },
        methods: {
            handle(a) {
                this.name = a
            },
            handleButton() {
                console.log(this.$refs.mychild.mytext);
                console.log(this.$refs.mychild.add(this.name));
            }
        },
    })
</script>

</body>
</html>

4、事件总线

不同层级的不同组件之间的通信

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <child1></child1>
    <hr>
    <child2></child2>
</div>


<script>
    // 定义一个事件总线
    var bus = new Vue();

    // 定义全局组件1
    Vue.component('child1', {
        template: `
        <div>
                <input type="text" v-model="text">
                <button @click="handleClick">点我传递数据到另一个组件</button>
        </div>

        `,
        data() {
            return {
                text: '',
            }
        },
        methods: {
            handleClick() {
                console.log(this.text);
                // 通过事件总线发送
                bus.$emit('senddata', this.text);
            },
        }
    });

    // 定义全局组件2
    Vue.component('child2', {
        template: `
        <div>
        收到的消息是:{{recv_text}}
        </div>
        `,
        data() {
            return {
                recv_text: '',
            }
        },
        mounted() {
            // 组件挂载(生命周期钩子函数中的一个),开始监听事件总线上的senddata
            bus.$on('senddata', (item) => {
                console.log('收到数据: ', item);
                this.recv_text = item;
            })
        }
    });

    var vm = new Vue({
        el: '#box',
        data: {},
        methods: {},
    })
</script>

</body>
</html>

5、动态组件

1、<component> 元素,动态地绑定多个组件到它的 is 属性
2、<keep-alive> 保留状态,避免重新渲染
5.1、component
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <ul>
        <li @click="who='child1'">首页</li>
        <li @click="who='child2'">商品</li>
        <li @click="who='child3'">订单</li>
    </ul>
    <component :is="who"></component>
</div>


<script>

    var vm = new Vue({
        el: '#box',
        data: {
            who: 'child1',
        },
        components: {
            child1: {
                template: `
                    <div>我是首页</div>
                `
            },
            child2: {
                template: `
                    <div>我是商品页</div>
                `
            },
            child3: {
                template: `
                    <div>我是订单页</div>
                `
            }
        },
    })
</script>

</body>
</html>
5.2、keep-alive
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <ul>
        <li @click="who='child1'">首页</li>
        <li @click="who='child2'">商品</li>
        <li @click="who='child3'">订单</li>
    </ul>
    
    <keep-alive>  <!--加上keep-alive后input框中的数据在点击其他按钮后还存在-->
        <component :is="who"></component>
    </keep-alive>
</div>


<script>

    var vm = new Vue({
        el: '#box',
        data: {
            who: 'child1',
        },
        components: {
            child1: {
                template: `
                    <div>我是首页    <input type="text"></div>
                `
            },
            child2: {
                template: `
                    <div>我是商品页</div>
                `
            },
            child3: {
                template: `
                    <div>我是订单页</div>
                `
            }
        },
    })
</script>

</body>
</html>

6、slot插槽

1、单个slot 
2、具名slot
	*混合父组件的内容与子组件自己的模板 ---> 内容分发
	*父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
6.1、基本使用
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <child1>
        <ul>
            <li v-for="item in 4">{{item}}</li>
        </ul>
    </child1>
    <child2></child2>
    <child3></child3>
</div>


<script>

    var vm = new Vue({
        el: '#box',
        data: {},
        components: {
            child1: {
                template: `
                    <div>
                        <slot></slot>
                        <hr>
                        我是首页
                    </div>
                `
            },
            child2: {
                template: `
                    <div>我是商品页</div>
                `
            },
            child3: {
                template: `
                    <div>我是订单页</div>
                `
            }
        },
    })
</script>

</body>
</html>
6.2、插槽小案例

一个组件通过插槽控制另一个组件的显示隐藏

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <child1>
        <button @click="isShow=!isShow">点我显示/隐藏child2</button>
    </child1>

    <child2 v-if="isShow"></child2>
</div>


<script>

    var vm = new Vue({
        el: '#box',
        data: {
            isShow: true,
        },
        components: {
            child1: {
                template: `
                    <div>
                        <slot></slot>
                    </div>
                `,
            },
            child2: {
                template: `
                 <div>
                    <ul>
                    <li v-for="i in 4">{{i}}</li>
                    </ul>
                 </div>
                `
            },
        },
    })
</script>

</body>
</html>
6.3、具名插槽

指定标签放到组件的某个插槽中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="js/vue.js"></script>


<div id="box">
    <child1>
        <button @click="isShow=!isShow" slot="button1">点我显示/隐藏child2</button>

        <div slot="div1"></div>
    </child1>

    <child2 v-if="isShow"></child2>
</div>


<script>

    var vm = new Vue({
        el: '#box',
        data: {
            isShow: true,
        },
        components: {
            child1: {
                template: `
                <div>
                <slot name="button1"></slot>
                <hr>
                我是华丽的分割线
                <hr>
                <slot name="div1"></slot>
                </div>
                `,
            },
            child2: {
                template: `
                 <div>
                    <ul>
                    <li v-for="i in 4">{{i}}</li>
                    </ul>
                 </div>
                `
            },
        },
    })
</script>

</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值