当前位置:   article > 正文

组件理解_html组件的理解

html组件的理解

大部分组件相同的三大特性:

  • props:定制数据
  • slot:定制结构
  • 组件通信(订阅发布模式)

定义组件

类比系统提供的组件:

  • 比如input,select,textarea等等
  • 共同特点:有差不多的样式,定制化,比如input的type属性传入text,button color(拾色器) date(日历)等等,有定制的功能

定义组件,相当于自己定义了一个标签,系统不认识,是借助于框架来解析的

prop

  • 组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,我们需要通过子组件的 props 选项。

  • 子组件要显式地用 props 选项声明它期待获得的数据:

  • props 里声明的数据在组件中写上驼峰命名法,因为属性名要符合变量名的命名规则;在行间中写烤串命名法,

prop验证

为组件的 props 指定验证规格

要指定验证规格,需要用对象的形式,而不能用字符串数组:

  • type 可以是下面原生构造器:

  • type 也可以是一个自定义构造器函数,使用 instanceof 检测。

  • 在props里 ,如果传递的type类型是对象,其默认值必须设置为函数,这个函数return出默认的对象

    • 注意:return的是一个对象,里面要写键值对的形式,
    • 比如下面的例子里的list,在template里用[1,2,3]时,注意{{list.lis}}这样才能找到[1,2,3]
  • 注意 props 会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用。

  • 还可以不用type,自定义验证规则

      Vue.component('custom-dialog', {
      	//props:['title'],
      	props: {
      		title: {
      			type:String,
      			default: '标题'
      		},
      		num: {
      				validator(value){             // 自定义验证规则
      					console.log(value);
      					return value > 10;
      				}
      		},
      		num: [Number,String,Array],
      		okValue:{
      			type: String,
      			required: true
      		},
      		list: {
      			type: Object,   // 类型为对象,默认值default必须设置为函数,在函数中返回默认的对象
      			return {
      				lis:[1,2,3]
      			}
      		}
      	}
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

组件之间的通信-订阅发布模式

  • 子组件向父组件通信:用自定义事件

  • 在子组件中发布一个事件,父组件如果关心这个事件,就会注册这个事件的处理函数(监控这个事件)

  • 在组件里不应该做具体的事情,应该把选择权交给使用者

  • 怎么做到?发生某个事件时,不自己做具体的事情,而是通知父组件,告诉它我的某个状态要变化了,关心的话就监控(绑定点击的事件处理函数)

类比系统提供的组件:

  • 比如input,select,textarea等等

  • 共同特点:有差不多的样式,定制化,比如input的type属性传入text,button color(拾色器) date(日历)等等,有定制的功能

      <input type="text" name="" value="123" a=10>
      <input type="text" name="" value="456">
      <input type="color" id="html5colorpicker" onchange="clickColor(0, -1, -1, 5)" value="#ff0000" style="width:85%;">
      <input type="date" name="user_date" />
    
    • 1
    • 2
    • 3
    • 4

比如两个下拉框

  • 共同的功能是点击时下拉框出现,这是内部的状态,不会影响外部;

  • 如果外部想根据选择的值相应的做一些事情,怎么知道什么时候值变了?答:监控onchange事件,这个事件是系统提供的

  • 自己定义的组件没有现成的事件,所以仿照系统的事件自定义一个事件;

      <select id="selectNode">
      	<option>567</option>
      	<option>123</option>
      </select>
      <select id="selectNode2">
      	<option>567</option>
      	<option>123</option>
      </select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

简易弹框组件的顶订阅发布模式分析(完整代码见最后)

需求:

  • 第一个弹框 点击确定按钮:隐藏div1
  • 第二个弹框 点击确定按钮:控制div2的颜色

分析

  • 1.因为是弹框的点击事件触发的一系列操作,所以由弹框组件c-dialog发布消息(点击确定按钮时发布自定义的confirm事件)

  • 2.弹框1的确定按钮想要控制的是div1,div1不是组件里的元素,是全局的元素,所以要在new Vue里操控。

    • 也就是说new Vue关心弹框1的确定按钮什么时候被点击了
    • 因而new Vue把一个自己的函数confirmFn1放在confirm事件上,监控confirm事件什么时候发生
    • 一旦监听到confirm事件发布,就执行函数confirmFn1,隐藏div1
  • 3.弹框2的确定按钮想要控制的是div2,div2是组件c-button的元素,所以由c-button操控,因而c-button把一个自己的函数confirmFn2放在confirm事件上,监控confirm事件什么时候发生

    • 一旦监听到confirm事件发布,就执行函数confirmFn2,改变div2颜色

通俗版分析

div1是在 点击弹框1的确定按钮时 隐藏,所以要知道弹框1的确定按钮什么时候被点击了

div2是在 点击弹框2的确定按钮时 变颜色,所以要知道弹框2的确定按钮什么时候被点击了

div1不在c-dialog组件里,而是在c-dialog组件的父组件里
div2也不在c-dialog组件里,在c-button组件里(也就是c-dialog的父组件里)

怎么知道?

父子之间的通信方法

父组件想知道子组件的确定按钮啥时候被点击,父子约定一个自定义事件ok。

对于弹框1:
爸爸相当于new Vue这个实例对象,孩子是c-dialog组件
爸爸派一个自己的亲信(confirmFn1函数)绑定在孩子的ok事件上,监听ok事件

孩子在自己的确定按钮上绑一个confirmHandle函数,监听按钮啥时候被点击。按钮被点击时,confirmHandle函数执行,通过$emit发布confirm事件,告诉爸爸:“confirm事件触发了,您可以做想做的事情了(把您的div1隐藏)”;
爸爸派的亲信confirmFn1函数监听到孩子喊的话,开始执行,把div1隐藏

c-button组件想知道弹框2的确定按钮啥时候被点击
对于弹框2:
爸爸是c-button组件,孩子是弹框2(c-dialog组件)

爸爸派一个自己的亲信(confirmFn2函数)绑定在孩子的confirm事件上,监听ok事件
弹框2确定按钮被点击时,confirmHandle函数执行,通过$emit发布ok事件,告诉爸爸:“confirm事件触发了,您可以把您的div2改颜色了”;

爸爸派的亲信confirmFn2函数监听到孩子喊的话,开始执行,把div2改颜色

定制结构

制的结构要写在组件的标签对之间,参考原生的标签fieldset

定制方法

  • 定制的结构要写在组件的标签对之间,参考原生的标签fieldset
  • 但是只写在组件的标签对之间,页面里不会渲染,要用slot标签

定制的结构插入到哪里?

  • vue提供了一个插槽标签" ,这对标签的位置就是要插入的位置
    <slot></slot>

  • slot标签放在哪,就会把组件的标签对之间的结构全部放在哪(注意:行间有slot属性的结构除外!!)

  • 如果没有slot标签,组件标签对之间的结构就会丢弃

  • 在 标签中的任何内容都被视为备用内容:

    • 如果在组件的<content></content>标签对里里放入<slot>1111111<slot>,1111111就是备用内容
    • 如果没有在组件的标签对里写内容,就默认是1111111

除非子组件模板包含至少一个 <slot插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的插槽时,父组件整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。

最初在 <slot标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。

不具名slot和具名slot

不具名slot

放在哪,就会把组件的标签对之间的结构全部放在哪

如果不想把标签对之间的结构放在slot标签所在的位置,想要放在其他位置(想要定义多个地方的结构),就要用到具名slot

具名slot

<slot>元素可以用一个特殊的属性 name 来配置如何分发内容。多个 slot 可以有不同的名字,具名 slot 将匹配内容片段中有对应 slot 特性的元素。

仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot,这些找不到匹配的内容片段将被抛弃。

比如:

想要把某个标签对里的结构换成自己定制的结构:

  1. 把footer的内容用slot包起来,给slot标签起一个名字
  2. 想用谁替换slot标签里的结构,就在谁的行间叫一下这个slot的名字

例子:把两个button按钮替换成一个p标签

	<slot name="footer-slot">
		<button>{{cancelValue}}</button>
		<button @click="okHandle">{{okValue}}</button>
	</slot>

	<p slot="footer-slot">我是第二个定制的content结构</p>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

具名slot的批量插入或替换

用template标签包起来,整体在template标签的行间叫一下这个slot的名字

比如下面例子:哪个slot标签的名字是footer-slot,就把哪个slot标签对里的结构内容替换为这几个span标签

	<template slot="footer-slot">
		<span>我是按钮</span>
		<span>我是按钮</span>
		<span>我是按钮</span>
		<span>我是按钮</span>
	</template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

###非父子组件之间的通信
在简单的场景下,可以使用一个空的 Vue 实例作为中央事件总线

  • 组件1关心组件2的某个状态,组件2的该状态变化时,通知组件1
  • 怎么通知?
    • 让bus帮忙发布消息(类似于广播)
    • 触发一个函数来通知
  • bus.$emit表示发布
  • bus.$on表示注册

例子:custom-test2关心custom-test1的button什么时候被点击

	var bus = new Vue();//中央事件总线		
	Vue.component("custom-test1", {
		template: `
			<button @click="okHandle">确定</button>
		`,
		//组件custom-test1发生点击事件时,okHandle执行,让bus帮忙发布ok事件(类似于广播)
		methods: {
			okHandle(){
				console.log("我点了确定");
				bus.$emit("ok");
			}
		}
	})
	Vue.component("custom-test2", {
		mounted(){
			//通过bus订阅了ok事件,并且把自己的handle函数作为ok的事件处理函数绑在ok事件上
			//这样,一旦监听到ok事件,就会执行this.handle函数,做一些事情
			bus.$on("ok",this.handle)
		},
		template: `
			<button>返回</button>
		`,
		methods: {
			handle(){
				console.log("我监听到了你的的变化");
			}
		}
	})
	new Vue({
		el: '#box'
	})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

类比理解:
函数fn1和fn2都要使用a变量,那么a变量应该放在二者都能访问到的位置

		let a = 1;
		function fn1(){
				
		}

		function fn2(){
				
		}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

简易弹框组件

html

<div id="box">
    <div id="div1" ref="div1">控制我隐藏</div>
    <c-dialog 
        title="警告" 
        confirm-value="确定" 
        num="110"
        @confirm="confirmFn1"
    >
        <ul>
            <li>我是定制的content里的结构</li>
            <li>我是定制的content里的结构</li>
        </ul>
    </c-dialog>
    
    <!-- 注意,num="0"的0代表字符串 :num="0"的0代表数字  v-bind的引号里会被解析为js表达式 -->
    <c-button></c-button>
    <!-- 注意一定要使用烤串命名法,否则会报错 -->
</div>



//定义一个组件
Vue.component('c-dialog',{
    //Prop验证
    props:{
        title:{
            type:String,
            default:'标题'
        },
        //num可以是字符串,数字,数组类型
        //num:[String,Number,Array],
        //自定义验证规则
        num:{
            validator(value){
                //value是外面num=""传进来的内容,验证传进来的内容是否合法
                console.log(value);
                return value>10;  //自定义的条件,符合这个条件,验证成功,否则失败(报错)
            }
        },
        confirmValue:{
            type:String,
            required:true  //必填
        },
        list:{
            type:Object, 
            //如果传递的类型是对象,其默认值default必须设置为函数,在这个函数里return默认的对象
            default(){
                return {}
            }
        }
    },
    //想要在content里定制一个ul列表
    //想要把第二个弹框的footer的结构换成自己定制的结构:把footer的内容用slot包起来,给slot标签起一个名字,想用谁替换slot标签里的结构,就在谁的行间叫一下这个slot的名字
    //批量替换:用template标签包起来,整体在template标签的行间叫一下这个slot的名字
    /*
        - slot标签放在哪,就会把组件的标签对之间的结构全部放在哪(注意:行间有slot属性的结构除外!!)
        - 如果没有slot标签,组件标签对之间的结构就会丢弃
        - 在 <slot> 标签中的任何内容都被视为备用内容:
            - 如果在组件的`<content></content>`标签对里里放入`<slot>1111111<slot>`,1111111就是备用内容
            - 如果没有在组件的标签对里写内容,就默认是1111111
    */
    template:`
            <div class="dialog">
                <h2>{{title}}</h2>
                <span>{{num}}</span>
                <div class="content">
                    我是内容  //不会被定制的结构覆盖
                    <slot>1111111111111111111111</slot>
                </div>
                <div class="footer">
                    <slot name="footer-slot">
                        <button @click="confirmHandle">{{confirmValue}}</button>
                        <button>取消</button>
                    </slot>
                </div>
            </div>
    `,
    methods:{
        confirmHandle(){
            this.$emit('confirm');
        }
    }
});

Vue.component('c-button',{
    template:`
        <div>
            <button>测试弹框</button> 
            <div ref="div2">控制我的颜色</div>
            <c-dialog confirm-value="ok" @confirm = "confirmFn2">
                <table>
                    <tr>
                        <th>姓名</th>
                        <th>姓名</th>
                        <th>姓名</th>
                        <th>姓名</th>
                    </tr>
				</table>
                <p slot="footer-slot">我是第二个定制的content结构</p>
                <template slot="footer-slot">
					<span>我是按钮</span>
					<span>我是按钮</span>
					<span>我是按钮</span>
					<span>我是按钮</span>
				</template>
            </c-dialog>   
        </div>
    `,
    methods:{
        confirmFn2(){
            this.$refs.div2.style.background="green";
        }
    }
});

new Vue({
    el:'#box',
    methods:{
        //想要隐藏的div是全局的div,不再c-dialog组件里,所以要通过父组件操作
        confirmFn1(){
            this.$refs.div1.style.display = 'none';
        }
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/636355
推荐阅读
相关标签
  

闽ICP备14008679号