自定义组件、全局和局部组件、组件之间的传值、插槽以及动态组件

本文详细介绍了Vue.js中组件的创建与使用,包括自定义组件、局部与全局组件的注册、组件间的传值方式、插槽的运用以及动态组件的实现。通过实例解析了组件的定义、事件处理和模板结构,帮助开发者更好地理解和应用Vue组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、自定义组件

组件是你能够拿来用的那些标签、方法等都可以叫做组件。
例如:h5标签可以说是系统自带的一个组件,html内置的组件。

那么如何用vue实现一个自定义组件呢?以下是实现过程:
1、首先定义一个组件:
其中:
(1)template表示当前组件显示的模板内容,
txt会在template这里模板html中显示
(2)

		//定义一个组件
        const Button = {
            //组件的模板html 
            template:`<div class="btn">{{txt}}</div>`, //template表示当前组件显示的模板内容
            //txt会在template这里模板html中显示
            props:['txt'],//txt属性 调用组件的时候传递过来
        };

2、注册组件:

		const app = new Vue({
            el:'#app',
            //通过components注册组件
            components:{
                btn:Button,
                //btn是在使用这个组件的时候的属性名 
           		//Button是上面定义组件时定义的那个变量
            }
        })

3、使用这个组件

	<div id="app">
        <btn txt="按钮"></btn>
        <btn txt="登录"></btn>
    </div>

css样式:

	.btn {
        background-color: deeppink;
        color: #fff;
        padding: 0.5rem;
        width: 80px;
        text-align: center;
        cursor: pointer;
        border-radius: 0.3rem;
        margin: 0.2rem;
      }
      .btn.danger {
        background-color: red;
      }
      .btn.info {
        background-color: greenyellow;
      }
例子: 下面再定义一个计数器的组件,同上:

1、定义组件:

		const Counter = {
            template:`<div class="counter">
                    <button @click="count += 1">计数-{{count}}</button>
                </div>`,
            //组件中的data必须是一个function,其返回一个对象作为组件的数据
            data(){
                return {
                    count:1,
                };
            }
        }

2、注册组件:

 		const app = new Vue({
            el:'#app',
            //注册组件
            components:{
                //组件在页面中使用的名字;组件对象
                btn:Button,
                //btn是在使用这个组件的时候的属性名 
                //Button是上面定义组件时定义的那个变量
                counter:Counter,
            }
        })

3、使用组件:

	<div id="app">
        <btn txt="按钮" type="danger"></btn>
        <btn txt="登录" ></btn>
        <counter></counter>
    </div>

结果如下:
在这里插入图片描述
若想再给计数换个刚刚自定义按钮,则可以写成如下形式:
其中还要在Counter里面再次进行注册

			const Counter = {
            template:`<div class="counter">
                    <button @click="count += 1">计数-{{count}}</button>
                   <btn txt="计数" @btn-click="clickHandle()"></btn>
                    </div>`,
            //组件中的data必须是一个function,其返回一个对象作为组件的数据
            data(){
                return {
                    count:1,
                };
            },
            //组件可以嵌套,在使用局部组件的时候需要进行注册
            components:{
                btn:Button,
            }
        }

总代码:

在定义的Button组件template中加了@click=“clickHandle”,并添加methods的clickHandle方法

//往外派发一个自定义事件btn-click,所有使用组件的父组件可以进行事件监听
this.$emit(‘btn-click’)

在定义的Counter组件template中加了@btn-click="clickHandle(),并添加methods的clickHandle方法

//此处的btn-click是属于组件的自定义事件,自定义事件需要使用$emit进行触发

总结:首先{<div @click=“clickHandle” class=“btn” :class=“type”>{{txt}}}定义了一个自定义button按钮,这个button其实就是一个div,div里有个文本内容,当点击这个div之后,类似于触发了一个click事件,这个click事件又向外派发了一个自定义事件,这个Counter组件监听一下这个自定义事件之后触发它的clickHandle函数。
自定义组件
</div>

<script src="./libs/vue.js"></script>
<script>
    //定义一个组件
    const Button = {
        //组件的模板html 
        template:`<div @click="clickHandle" class="btn" :class="type">{{txt}}</div>`,
        props:['txt','type'],//属性 调用组件的时候传递过来,父组件传递过来的参数
        methods:{
            clickHandle(){
                // alert('111')
                //往外派发一个自定义事件btn-click,所有使用组件的父组件可以进行事件监听
                this.$emit('btn-click')
            }
        }
    };

    //此处的btn-click是属于组件的自定义事件,自定义事件需要使用$emit进行触发
    const Counter = {
        template:`<div class="counter">
                <button @click="count += 1">计数-{{count}}</button>
                <btn txt="计数" @btn-click="clickHandle()"></btn>
                </div>`,
        //组件中的data必须是一个function,其返回一个对象作为组件的数据
        data(){
            return {
                count:1,
            };
        },
        //组件可以嵌套,在使用局部组件的时候需要进行注册
        components:{
            btn:Button,
        },
        methods:{
            clickHandle(){
                alert('1111');
                
            }
            
        }               

    }

    const app = new Vue({
        el:'#app',
        //注册组件
        components:{
            //组件在页面中使用的名字;组件对象
            btn:Button,
            //btn是在使用这个组件的时候的属性名 
            //Button是上面定义组件时定义的那个变量
            counter:Counter,
        },
        
    })
</script>
结果图:

在这里插入图片描述

二、局部组件和全局组件

我们在开发的时候遵循的原则:
(1)把经常复用的内容封装成一个组件,
(2)把逻辑复杂的一些功能封装成组件
在vue中定义组件有两种常见的形式
1、局部组件
const c = { template:`` }
局部组件在使用中需要注册
components属性 进行注册
2、全局组件
Vue.component(’’,{ template })
全局组件定义之后可以直接使用不需要注册

全局组件
<div id="app">
  <count></count>
  <hello></hello>
</div>
<hr>    
<div id="app1">
  <count></count>
  <hello></hello>
</div>

、、

 //定义一个全局组件
      Vue.component('hello', {
        template: `
        <div>
            <h1>Hello 小明</h1>
            <p>小明是一个好孩子</p>
        </div>
        `
      });
	var app = new Vue({
        el: '#app',
        components: {
          count: Counter
        }
      });

      new Vue({
        el: '#app1',
        
      });

上面再复制一个app1,结果如下:控制台报错,下面那个局部组件没有注册,(我是一个计数器)不显示。全局的依然可用。
在这里插入图片描述

局部组件:

//定义局部组件,就是一个对象,此对象包含有一些特殊的属性
// template 模板内容
// 组件的数据,data必须是一个function,此function有一个对象作为返回值
// methods 方法
// computed 计算属性
// … 其他的等等等等,所有vue中可以使用的方法或者属性都可以在组件内部使用

定义全局组件用component属性

//定义一个全局组件
    Vue.component('hello',{
        template:`
            <h1>Hello 小明</h1>
            <p>小明是一个好孩子</p>
        `
    })

以上这样写会报错,需要在这两个同级的元素外面再加上一个根节点
因此组件定义的时候需要遵循一个原则:
定义的的组件必须只有一个根节点

定义一个计数器,

 		var Counter = {
            template: `<div class="counter">
                    <h1>我是一个计数器:{{count}}</h1>
                    <p>步长:{{step}}</p>
                    <button @click="count+=step">点击</button>
                </div>`,
            props:['step'],
            data(){
                return {
                    count:0,
                }
            }
          };
	 var app = new Vue({
        el: '#app',
        components: {
          count: Counter
        }
      });

在页面中使用这个组件时,想要让它根据步长来增加,但是如果步长是字符串直接拼接到后面出现不了想要的效果,因此具体情况如下:

 <div id="app">
        <!-- step="2"中的2是一个字符串 
        :step="2"表示是一个js对象,此时的2是数字
        如果想要它再变成字符串,:step="'2'" -->
      <count :step="'2'"></count>
      <hello></hello>
    </div>

现在把props可以写成对象的形式:

//props以对象的形式设置属性 可以对传递的数据做验证
        //如果不符合直接报错
        props:{
            step:Number,
        },
三、组件之间的传值
1、父组件向子组件传值

父组件向子组件传值,使用props属性进行传值

		var Counter = {
        template: `<div class="counter">
                <h1>我是一个计数器:{{count}}</h1>
                <p>步长:{{step}}</p>
                <button @click="count+=step">点击</button>
            </div>`,
        // props:['step'],
        //props以对象的形式设置属性 可以对传递的数据做验证
        //如果不符合直接报错
        props:{
            step:Number,//属性名:数据类型
        },
        data(){
            return {
                count:0;    
            }
        }
      };

在这里插入图片描述

2、子组件向父组件传值

子组件向父组件传值,使用事件派发

 	var Counter = {
        template: `<div class="counter">
                <h1>我是一个计数器:{{count}}</h1>
                <p>步长:{{step}}</p>
                <button @click="addHandle">点击</button>
            </div>`,
        // props:['step'],
        //props以对象的形式设置属性 可以对传递的数据做验证
        //如果不符合直接报错
        props:{
            step:Number,//属性名:数据类型
        },
        data(){
            return {
                count:0,
              
            }
        },
        methods:{
          addHandle(){
            this.count += this.step;
            //派发事件
            this.$emit('countchanged',this.count);
          }
        }
      };

、、父组件进行监听、接收

	var app = new Vue({
        el: '#app',
        components: {
          count: Counter,
        },
        data:{
            stepVal:2,
            fromCount:0,//为了动态的接收数据
        },
        methods:{
          // 接收组件传递的数据 定义了一个第三方变量thisfromCount,
          // 这个第三方变量等于我传递过来的数据进行显示
          countChandle(v){
            this.fromCount=v
          }
        }
        

      });
四、vue实现跨组件传值

vue实现跨组件传值主要通过eventBus,即事件总线。
简单理解就是:小明在A城市,小红在B城市,小兰要从A城市去往B城市,小明委托给小兰给小红带去一封信,回来的时候小红又委托给小兰给小明带封回信,这里的小红就相当于事件总线。
即:
1、 事件总线 解决非相关组件之间传值
2、 我们通过定义一个空白的对象 所有的时间派发和监听都在这个对象上进行

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>非相关组件之间传值</title>
    <style>
        img{
            width: 100px;
            max-height: 100px;
        }
        .lists ul{
            list-style: none;
            display: flex;
            align-items: center;
            justify-content: center;
            
        }
        .lists ul li{padding:10px;color:green}
        .product{min-height:150px;border:1px solid blanchedalmond}
    </style>
</head>
<body>
    <!-- 事件总线 解决非相关组件之间传值 -->
    <!-- 我们通过定义一个空白的对象 所有的时间派发和监听都在这个对象上进行 -->
    <div id="app">
        <varbar></varbar>
        <product v-for="(p,i) in list" :product="p" :key="i"></product>
    </div>
    <!-- 顶部导航组件 -->
    <!-- 商品列表组件 -->
    <script src="./libs/vue.js"></script>
    <script>
        const eventBus = new Vue();//定义一个空白的vue实例,用来做事件的转发
        const varbar = {
        template:`
            <div class="lists">
                <ul>
                    <li>首页</li>
                    <li>商品列表</li>
                    <li>购物车({{cartCount}})</li>
                    <li>其他</li>
                </ul>
            </div>       
           `,
           created(){
               //组件创建完成之后,监听一下这个buyHandle
               eventBus.$on('buy-product',this.buyHandle)
           },
           methods:{
               //然后接收一个参数product,这个参数由派发的p来
               buyHandle(product){
                   console.log(product);
                   this.cartCount +=1
               }
           },
           data(){
               return {
                   cartCount:0,
               }
           }

        };
        const product = {
            template:`
                <div class="product">
                    <img src=""/>
                    <h5>{{product.name}}</h5>
                    <p>{{product.price}}</p>
                    <button @click="buyHandle(product)">购买</button>
                </div>
            `,

            props:['product'],
            methods:{
                buyHandle(p){
                    eventBus.$emit('buy-product',p)//通过事件总线
                }
            }

        };
        const app = new Vue({
            el:'#app',
            data:{
                list:[
                    {img:'',name:'MH',price:200},
                    {img:'',name:'IPhone',price:300},
                    {img:'',name:'HuaWei',price:400},
                    {img:'',name:'Nokie',price:500},
                    {img:'',name:'DiA',price:600},

                ]
            },
            components:{
                varbar:varbar,
                product:product,
            },
            
        

        })
    
    </script>
    
</body>
</html>

在这里插入图片描述

五、插槽
1、slot 插槽 可以理解为占位符
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <hello>
            <h5>我是什么呢?</h5>
            <h5>你是什么呢?</h5>
        </hello>
    </div>
    <script src="./libs/vue.js"></script>
    <script>
        //slot 插槽 可以理解为占位符
        var hello = {
            template:`
            <div>
                我是组件内部的东西
                <slot></slot>
                <p>我也是组件内部的p标签</p>
            </div>`
        };
        var app = new Vue({
            el:'#app',
            components:{
                hello,
            }
        })

    </script>
</body>
</html>

运行结果:
在这里插入图片描述

2、具名插槽

具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
使用方法:v-slot:插槽名字

  <body>
      <div id="app">
        <hello>
            <h5>我是什么呢?</h5>
            <h5>你是什么呢?</h5>
            <template v-slot:f>
                <h3>我是f中的内容</h3>
            </template>
        </hello>
    </div>
    <script src="./libs/vue.js"></script>
    <script>
        //slot 插槽 可以理解为占位符
        //具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
        //v-slot:插槽名字
        var hello = {
            template:`
            <div>
                我是组件内部的东西
                <slot></slot>
                <p>我也是组件内部的p标签</p>
                <slot name="f"></slot>
            </div>`
        };
        var app = new Vue({
            el:'#app',
            components:{
                hello,
            }
        })

    </script>
</body>

运行结果:
在这里插入图片描述

3、vue中template标签

未使用template时

<body>
    <div id="app">
        <!-- 在vue中template节点在html中生成的时候不会显示出来 -->
        <div v-for="i in 10">
            <h5>我是一个人</h5>
            <p>我是一个人的详细信息</p>
        </div>

        <!-- <hello>
            <h5>我是什么呢?</h5>
            <h5>你是什么呢?</h5>
            <template v-slot:f>
                <h3>我是f中的内容</h3>
            </template>
        </hello> -->
    </div>
    <script src="./libs/vue.js"></script>
    <script>
        //slot 插槽 可以理解为占位符
        //具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
        //v-slot:插槽名字
        var hello = {
            template:`
            <div>
                我是组件内部的东西
                <slot></slot>
                <p>我也是组件内部的p标签</p>
                <slot name="f"></slot>
            </div>`
        };
        var app = new Vue({
            el:'#app',
            components:{
                hello,
            }
        })

    </script>

生成的html节点中多了一个div标签
在这里插入图片描述
使用template时

<body>
    <div id="app">
        <!-- 在vue中template节点在html中生成的时候不会显示出来 -->
        <template v-for="i in 10">
            <h5>我是一个人</h5>
            <p>我是一个人的详细信息</p>
        </template>
        
        <!-- <hello>
            <h5>我是什么呢?</h5>
            <h5>你是什么呢?</h5>
            <template v-slot:f>
                <h3>我是f中的内容</h3>
            </template>
        </hello> -->
    </div>
    <script src="./libs/vue.js"></script>
    <script>
        //slot 插槽 可以理解为占位符
        //具名插槽 通过name可以为插槽设置一个名字 在其中填充内容的时候使用一个template标签
        //v-slot:插槽名字
        var hello = {
            template:`
            <div>
                我是组件内部的东西
                <slot></slot>
                <p>我也是组件内部的p标签</p>
                <slot name="f"></slot>
            </div>`
        };
        var app = new Vue({
            el:'#app',
            components:{
                hello,
            }
        })

    </script>

在这里插入图片描述

六、动态组件

通过component实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .container{
            padding:0.5rem;
            border:0.02rem solid deeppink;
        }
    </style>
</head>
<body>
    <div id="app">
        <!-- <a href="tel:119">拨打电话:119</a> -->
        <!-- Javascript:void(0) 不刷新 -->
        <a href="Javascript:void(0)" @click="currentComponent='home'">【首页】</a>
        <a href="Javascript:void(0)" @click="currentComponent='list'">【列表】</a>
        <a href="Javascript:void(0)" @click="currentComponent='about'">【关于我们】</a>
        <a href="Javascript:void(0)" @click="currentComponent='me'">【个人中心】</a>
        <br>
        <div class="container">
            <!-- 动态组件 使用is设置当前显示的组件内容 -->
            <component :is = "currentComponent"></component>
        </div>
    </div>
    <script src="./libs/vue.js"></script>
    <script>
        const Home = {
            template:`<h1>我是Home</h1>`
        };
        const List = {
            template:`<h1>我是List</h1>`
        };
        const About = {
            template:`<h1>我是About</h1>`
        };
        const Me = {
            template:`<h1>我是Me</h1>`
        };
        var app = new Vue({
            el:'#app',
            components:{
                home:Home,
                list:List,
                about:About,
                me:Me,
            },
            data:{
                currentComponent:'home',
            }
        })

    </script>
</body>
</html>

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值