【Vue】Vue组件


Vue

三、Vue组件

01.组件注册

(1)组件基本概念
  • 基本方法:Vue.component("组件名称",{})
    • 第一个参数是标签名称,第二个参数是一个选项对象
  • 组件注意事项:
    • 组件参数的data值必须是函数,同时这个函数要返回一个对象
    • 组件模板必须是单个根元素
    • 组件模板的内容可以是模板字符串
    • 如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,在普通的标签模板中,必须使用短横线的方式使用组件
(2)全局组件注册
<div id="app">
    <!-- 
		组件可以重复使用多次,因为data中返回的是一个对象所以每个组件中的数据是私有的
		即每个实例可以维护一份被返回对象的独立的拷贝 
	--> 
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>

    <!-- 驼峰命名方式注册的组件,在普通标签模板中,必须使用短横线的方式使用组件,因为HTML不区分大小写 -->
    <hello-world></hello-world>
</div>

<script>
    //  如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,
    //  但是在普通的标签模板中,必须使用短横线的方式使用组件
    Vue.component('HelloWorld', {
        // data值必须是函数,同时这个函数要求返回一个对象
        data: function(){
            return {
                msg: 'HelloWorld'
            }
        },
        // 必须是单个根元素
        template: '<div>{{ msg }}</div>'
    });

    Vue.component('button-counter', {
        data: function(){
            return {
                count: 0
            }
        },
        //  组件模板的内容可以是模板字符串,在字符串模板中可以使用驼峰命名的方式使用组件	
        template: `
            <div>
                <button @click="handle">点击了{{count}}次</button>
                <HelloWorld></HelloWorld>
    		</div>
		`,
        methods: {
            handle: function(){
                this.count += 2;
            }
        }
    })
    let vm = new Vue({
        el: '#app',
        data: {}
    });
</script>
(3)局部组件注册
<div id="app">
    <my-component></my-component>
</div>
<script>
    //	定义组件的模板
    var Child = {
        template: '<div>A custom component!</div>'
    }
    let vm = new Vue({
        el: "#app",
        //	注册局部组件  
        components: {
            // <my-component> 将只能在父模板可用,一定要在实例上注册了才能在html文件中使用
            'my-component': Child
        }
    })
</script>

02.组件传参

(1)父组件向子组件传值
  • 组件内部通过props接收传递过来的值

  • 父组件通过属性将值传递给子组件

    • 父组件发送的形式是以属性的形式绑定值到子组件身上

    • 然后在子组件中用属性props接收

      如果在props中使用驼峰形式接收参数,那么在模板中需要使用短横线的形式,字符串模板中没有这个限制

    <div id="app">
        <div>{{pmsg}}</div>
        <!-- 1、menu-item 在#app中嵌套,故 menu-item 为子组件      -->
        <!-- 给子组件传入一个静态的值然后再子组件的注册中,用props属性接受这个值 -->
        <menu-item title='来自父组件的值'></menu-item>
        <!-- 2、可以动态绑定属性,此时ptitle是来自父组件data中的数据,传的值可以是数字、对象、数组等等 -->
        <menu-item :title='ptitle' content='hello' menu-title='短横线'></menu-item>
    </div>
    
    <script>
        Vue.component('menu-item', {
            // 3、 子组件用属性props接收父组件传递过来的数据  
            props: ['title', 'content', 'menuTitle'],
            data: function() {
                return {
                    msg: '子组件本身的数据'
                }
            },
            template: '<div>{{msg + "----" + title + "-----" + content + menuTitle}}</div>'
        });
        let vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中内容',
                ptitle: '动态绑定属性'
            }
        });
    </script>
    
(2)子组件向父组件传值
  • props传递数据原则:单向数据流

  • 子组件通过自定义事件向父组件传递消息

    • 子组件用$emit("自定义事件名称",传递的数据)触发事件
  • 父组件监听子组件的事件

    • 父组件用v-on监听子组件的事件
    <div id="app">
        <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
        <!-- 
    		在父组件上用v-on 监听子组件的事件
    		这里 enlarge-text 是子组件中的 $emit 自定义事件中的第一个参数
    		对应 handle 为父组件对应的事件处理函数,即Vue实例中的事件处理函数
    	-->
        <!-- enlarge-text 表示子组件中自定义事件,$event 表示子组件传递过来数据的值 -->
        <menu-item @enlarge-text='handle($event)'></menu-item>
    </div>
    
    <script>
        // 子组件向父组件传值-携带参数
        Vue.component('menu-item', {
            // 子组件用$emit()触发事件
            // 第一个参数为自定义的事件名称,第二个参数为需要传递的数据  
            template: `
                <div>
                    <button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>
                    <button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小</button>
        		</div>
            `
        });
        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件中内容',
                fontSize: 10
            },
            methods: {
                handle: function(val){
                    this.fontSize += val;
                }
            }
        });
    </script>
    
(3)非父子组件间的传值
  • 需要通过单独的事件中心管理组件间的通信:let eventHub = new Vue()

  • 监听事件与销毁事件:$on()$off()

  • 触发事件:$emit()

    <div id="app">
        <div>父组件</div>
        <div>
            <button @click='handle'>销毁事件</button>
        </div>
        <test-A></test-A>
        <test-B></test-B>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>
    <script type="text/javascript">
        //1、 提供事件中心,一个空Vue实例
        var hub = new Vue();
    
        Vue.component('test-A', {
            data: function(){
                return {
                    num: 0
                }
            },
            template: `
                <div>
                    <div>A:{{num}}</div>
                    <div>
                    	<button @click='handle'>点击</button>
                    </div>
                </div>
            `,
            methods: {
                handle: function(){
                    //2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)   触发兄弟组件的事件
                    hub.$emit('B-event', 2);
                }
            },
            mounted: function() {
                // 3、接收数据方,通过mounted(){} 钩子中  触发hub.$on(方法名
                hub.$on('A-event', (val) => {
                    this.num += val;
                });
            }
        });
        Vue.component('test-B', {
            data: function(){
                return {
                    num: 0
                }
            },
            template: `
                <div>
                    <div>B:{{num}}</div>
                    <div>
                   	 	<button @click='handle'>点击</button>
                    </div>
                </div>
            `,
            methods: {
                handle: function(){
                    //2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)   触发兄弟组件的事件
                    hub.$emit('A-event', 1);
                }
            },
            mounted: function() {
                // 3、接收数据方,通过mounted(){} 钩子中  触发hub.$on()方法名
                hub.$on('B-event', (val) => {
                    this.num += val;
                });
            }
        });
        var vm = new Vue({
            el: '#app',
            data: {
    
            },
            methods: {
                handle: function(){
                    //4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据  
                    hub.$off('A-event');
                    hub.$off('B-event');
                }
            }
        });
    </script>
    
    
    • 传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)
    • 接收数据方,通过mounted(){} 钩子中,触发hub.$on()方法名
    • 销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
(4)ref
  • ref放在标签上,可以获得原生节点

  • ref放在组件上,可以获得组件对象

    <div id="app">
        <div ref="mytext"> </div>
        <my-component ref="myComponent"></my-component>
        <button @click="handle">点击</button>
    </div>
    <script>
        Vue.component("my-component",{
            template: "",
            methods: {
                add: function(){
                    
                }
            }
        })
    	let vm = new Vue({
           methods: {
               handle: function(){
                   console.log(this.$refs.mytext);
                   // 可以直接访问子组件中的方法
                   console.log(this.$refs.myComponent.add());
               }
           } 
        });
    </script>
    

03.动态组件

  • Vue提供了一个内置组件<component>,可以通过is属性动态绑定组件

  • 通过<keep-alive>保持活性,即保留页面内容,避免再次渲染

    <keep-alive>
    	<component :is="who"></component>
    </keep-alive>
    
    <!-- 通过改变who值——组件名,来切换组件显示 -->
    

04.组件插槽

(1)作用
  • 在HTML的父组件标签中,向子组件传递内容
(2)基本用法
  • 插槽位置:使用<slot>标签来预留位置

    • 【注意】:如果没有<slot>标签,那么插槽内容无效
  • 插槽内容

<div>
    <!-- alert-box是父组件,插槽内容是插入父组件内部定义的模板中 -->
    <alert-box>这个就是插槽内容</alert-box>
</div>
<script>
	Vue.component('alert-box', {
      // 当组件渲染的时候,这个 <slot> 元素将会被替换为 “组件标签中嵌套的内容”
	  // 但如果没有这个 <slot> 元素,那么组件标签内部嵌套的内容无效
      template: `
        <div>
          	<slot>默认内容</slot>
        </div>
      `
    });
</script>
(3)具名插槽
  • 含义:即具有名字的插槽,需要使用<slot>中的 name属性来绑定元素,从而在指定了名字的插槽中插入内容
    • 而没有name属性的<slot>标签具有隐含的name属性,即name=default,同时没有绑定名字的内容都会被传入默认插槽中
    • 在向具名插槽传入内容的时候,需要在 <template> 元素上使用 v-slot 指令
    • v-slot可以简写为#,但后面必须有参数,如:#header#default
<div id="app">
    <base-layout>
        <!-- 
			通过v-slot指令来匹配具名插槽, 这个v-slot的值必须和下面slot组件的name值对应上
			如果没有匹配到,则放到匿名的插槽中,即没有name属性的插槽
			template临时的包裹标签最终不会渲染到页面上
		--> 	
        <template v-slot:header>
            <p>标题信息1</p>
            <p>标题信息2</p>
        </template>
        <!-- 这个标签没有指定v-slot指令,那么这里的内容会被插入匿名插槽中,即没有指定name属性的插槽 -->
        <p>主要内容1</p>
        <p>主要内容2</p>
        <!-- 或者v-slot指令也可以用slot属性来指定,即 slot="...",已废弃 -->
        <template slot='footer'>
            <p>底部信息信息1</p>
            <p>底部信息信息2</p>
        </template>
    </base-layout>
</div>
<script>
    // 具名插槽
    Vue.component('base-layout', {
        // 使用 <slot> 中的 "name" 属性,指定当前插槽的名字
        // 如果 <slot> 元素没有 name 属性,则隐含带有 name=default
        // 具名插槽的渲染顺序,完全取决于【模板】的顺序,而不是取决于父组件中元素的顺序
        template: `
            <div>
                <header>
                    <slot name='header'></slot>
                </header>
                <main>
                	<slot></slot>
    			</main>
                <footer>
                    <slot name='footer'></slot>
                </footer>
            </div>
        `
    });
</script>
(4)作用域插槽
  • 父组件对子组件加工处理,让插槽内容访问子组件的数据
  • 既可以复用子组件的slot,又可以使slot内容不一致
<div id="app">
    <current-user>
        <!-- 
        	 父组件中,在<template>元素上,使用v-slot指令传递数据,v-slot:default="slotProps",
        	 slotProps在这里只是临时变量,以接收子组件在插槽上传递过来的数据
			这里是子组件上的 info 数据传入父组件中,存储在slotProps对象中
        --> 	
        <template v-slot:default='slotProps'>
                {{slotProps.info.name}}
        </template>
    </current-user>
</div>
<script>
    // 作用域插槽
    Vue.component('current-user', {
        props: ['list'],
        // 在子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法  :msg="xxx",
        // 这样,在使用组件时的HTML页面内,即父组件中,也可以访问到这个item数据
        // 插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供内容,会显示默认的内容。
        // 如果父组件为这个插槽提供了内容,则默认的内容会被替换掉
        template: `
            <div>
                    <slot :info='item'>
						{{item.name}}
    				</slot>
            </div>
        `
    });
</script>

(未完待续~~~)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值