JavaScript入门 组件/组件通讯:父传子/子传父/refs属性 Day03

组件


什么是组件

组件是可复用的 Vue 实例,且带有一个名字:我们可以把组件作为自定义元素来使用,
例如: <button-counter>。

组件是一个功能高内聚(组件的特性都应该有,比如按钮,可以有点击效果,用户可点击,可不让用户点击),业务低耦合(用户点击了按钮要做什么事情)的一个模块。高内聚的本质也就抽象和封装,目的是为了代码结构清晰,减少代码量。低耦合的目的是为了不同服务之间不同的业务代码不混用,降低了整体宕机的风险。

以不同的组件来划分不同的功能模块,需要什么功能就去调用对应的模块

1、每一个组件都应该使用.vue文件来命名

2、每一个组件都有一些模板

3、在这个文件中使用template来标记为一个html模板,在这里边编写html,和之前页面写法一致--template

4、通过之前的学习知道,createApp方法要接收一个对象,所以这个页面要导出一个对象--js --script

5、样式 -- style

vue文件在脚手架中运行的时候,会被编译为一个js代码,如果有template模板,那么它会被编译成一个render方法

scoped给样式添加作用域

给元素添加一个属性data-v-xxx,并且给样式添加属性选择.btn[data-v-782f4806]

<style scoped>
    .btn {
        display: inline-block;
        vertical-align: middle;
        line-height: 32px;
        text-align: center;
        font-size: 14px;
    }
</style>

【面试】为什么在vue组件中data要写成函数而不是对象?

        1. vue组件本质上是一个vue实例对象

        2. 如果data是对象,所有vue组件实例对象都会共享data对象数据,

             如果A组件改变了数据,B组件会受到影响.

        3. 定义成函数,函数有自己的块作用域,每个vue实例都有自己的data块作用域相互不影响

组件化和模块化的区别

组件化:是从UI界面的角度进行划分的,根据封装的思想,把页面上可重用的 UI,结构封装为组件,从而方便项目的开发和维护。

模块化:是从代码的逻辑角度去划分的 方便代码分层开发保证每个功能模块的职能单一

使用组件

vue中组件名字不能和原生元素名字一致

        <div id="app">
			<h2>{{title}}</h2>
			<!-- 使用组件 -->
			<button-counter></button-counter> //3.将组件名用-分开使用组件
		</div>
        <script>
			const { createApp } = Vue
			//1.构造定义组件
			// vue选项都能使用 data methods  computed watch template模板
			const ButtonCounter = { //驼峰命名
				template: `<div>
                         <button @click="plus">{{count}} 加一</button>
                      </div>`,
				data() {
					return {
						count: 0,
					}
				},
				methods: {
					plus() {
						this.count++
					},
				},
			}
			const app = createApp({
				data() {
					return {
						title: '组件学习',
					}
				},
			})
			// 2.注册组件  <button-counter></button-counter>
			app.component('ButtonCounter', ButtonCounter)
            app.component('ButtonMinus',ButtonMinus)
			app.mount('#app')  //挂载
		</script>

组件和子组件的生命周期

组件和子组件的生命周期顺序,是在父组件的mounted之前会去对子组件进行初始化和挂载(这里的挂载是子组件把它自己挂载到父组件上),最后再把所有的组件全部挂载到dom上

组件通讯


一个组件(A)引用另外一个组件(B),那么B组件就是A组件的子组件,A组件是B组件的父组件

父传子 props

props是只读属性,不要修改它!!!

子组件定义props选项,接收参数

父组件里定义属性传参, 属性名props选项接收参数属性名相同

<!--父-->
<div id="app">
     <child username="jack" :user="users"></child> 
</div> 
//child子组件
    props:['user'] 
    props:{ user:Object } 
    props:{ user:{ type:Object, defaule:{} } }
		<div id="app">
            <h2>{{title}}</h2> //显示'父组件'
            <child :msg="message" username="jack" :user="user"></child>
        </div><!--父组件定义属性传参--> 
		<script src="./lib/vue.global.js"></script>
		<script>
			const { createApp } = Vue
            //定义子组件child
            const Child = {
                props:['msg','username','user'],
                // props:{
                //     msg:{
                //         type:String,
                //         default:'helloworld'
                //     },
                //     username:String,
                //     user:Object
                // },
                data() {
                    return {
                        title: '子组件',
                    };
                },
                template:`<div class="child">
                             <h3>{{title}}</h3>
                             <p>{{msg}}</p>
                             <p>{{username}}</p>
                             <p>{{user}}</p>
                         </div>`
            }
			createApp({
                components:{
                    Child
                },
				data() {
					return {
						title: '父组件',
                        message:'这是来自父组件的内容',
                        user:{
                            name:'jack',
                            age:18
                        }
					}
				},
			}).mount('#app')
		</script>

注册组件时 在component里的注册组件是构造函数

在节点上的组件才是实例,所以父传子的参数要在节点的实例上传,即父组件中

子组件定义传递的属性的名字和类型、默认值、是否必传,父组件在子组件的标签上添加属性,如果没有使用绑定语法那么绑定是字符串(例如 user:"true"  true是字符串而不是布尔),如果要传递具体的数据对象,那么必须使用绑定语法(:user:"true")

<!--父组件-->
<template>
    <div>
        <h3>父子组件传值</h3>
        <button @click="parentCallback">我是一个按钮</button>
        <!-- 父组件给子组件传递参数--唯一的办法就是绑定属性 -->
        <!-- 绑定属性的时候,一定要注意,如果没有使用绑定语法,那么传递的都是普通属性 -->
        <!-- <QfButton qflabel="保存" showPre="true"/> -->
        <QfButton v-on:qfClick="parentCallback" qflabel="保存" :showPre="true"/>
        <QfButton/>   //组件实例化
        <QfButton qflabel="返回" :showPend="true"/>
    </div>
</template>
<script>
    import QfButton from './QfButton.vue'  //从子组件引入
    export default {
        // 注册的组件(QfButton)不是实例,是一个实例的构造函数--ComopentOptions--组件用于实例化的选项配置
        // 在页面使用的时候才进行实例化
        components: { QfButton},
        methods: {
            parentCallback() {
                console.log('-------------------------- 父组件回调方法被触发')
                console.log(arguments)
            }
        }
    }
</script>
<!--子组件-->
<template>
    <div @click="childCallback" class="qf-btn">
        <i v-if="showPre" class="pre"></i>
        {{qflabel}}
    </div>
</template>
<script>
    export default {
        // 那么接收父组件传递的数据,应该使用props属性来接收数据
        // 属性传递,是传递给实例的属性,那么页面上可以直接使用
        props: {
            // qflabel: String,
            qflabel: {
                type: String,
                // 设置成必须传递 如果父组件没有值传来就会报错
                required: true
            },
            showPre: {
                // 数据类型
                type: Boolean,
                // 指定默认值
                default: false
            }
        }
    }
</script>

子传父

$emit使用

​// $emit: (event: string, ...args: any[]) => void 定义$emit方法的使用规则
 // 第一个参数是event: string -> 事件名字;
 // ...args: any[] -> ...args 表示把参数展开,any[] 表示是数组,数组中的每一项可以是任意数据类型
 // 从第二个参数开始,为这个事件传递的参数
 this.$emit('qfClick', ...[1, 'string', true, {}, [], (function() {})])

​

 法一 在外部定义: 在子组件上绑定一个自定义事件 <子组件的@click"$emit('someEvent') ">

				template: `<div class="child">
                             <h3>{{title}}</h3>
                             <button @click="$emit('someEvent')">确定</button>
                         </div>`

 法二 在内部定义: this.$emit('someEvent',参数)  '事件名字',传的参数

       template: `<div class="child">
                    <h3>{{title}}</h3>
                    <button @click="onEmitEvent">确定</button>
                  </div>`
                 //组件Child 
                methods:{
                    onEmitEvent(){
                        //触发自定义事件
                        this.$emit('someEvent',this.count) //count是data()里的 这里的自定义事件用驼峰命名
                    }

在使用子组件的地方用刚刚自定义的事件,就是body里用-把驼峰命名分开的div里

@some-event="callback"

        <div id="app">
			<h2>{{title}}</h2>
            <p>显示子组件内容: {{num}}</p>
			<child @some-event="callback"></child>  //用-将事件的驼峰命名分开
		</div>
               methods:{  //父组件的方法里
                    callback(num){
                        // alert('触发自定义事件someEvent :'+num)
                        this.num = num
                    }
                }

$parent

 可以获取父组件实例 this.$parent===父组件的this
 最大的坏处是,人家用你的组件还必须起你定义的变量和方法名字
 this.$parent.pcb(3)       //直接调用父组件中的pcb()方法并传3

<!--父组件-->
<template>
    <div>
        <h3>父组件</h3>
        <h5>
            当前组件的值:{{value}}
        </h5>
        <Child @qfcb="pcb"/>
    </div>
</template>
<script>
    import Child from './Child.vue'
    export default {
        components: { Child },
        data() {
            return {
                value: 10
            }
        },
        methods:{
            pcb(step) {
                this.value += step   //点击+3
            },
        }
    }
</script>
<template>
    <div>
        <hr>
        <p>
            <button @click="myClickFunc">直接改变父组件的value值</button>
        </p>
        <h6>
            子组件的值:{{count}}
        </h6>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                count: 1
            }
        },
        methods: {
            myClickFunc() {
                // 可以获取父组件实例 this.$parent===父组件的this
                // this.$parent.value += 10
                // 最大的坏处是,人家用你的组件还必须起你定义的变量和方法名字
                this.$parent.pcb(3) //直接调用父组件中的pcb()方法并传3
            }
        }
    }
</script> 

refs属性

vue中不允许直接使用document.getElementById等方法来获取dom元素,应该使用关联属性(ref)来获取虚拟dom关联的真实dom

 非必须情况下不要使用,因为组件内部的数据和方法是组件自己封装的,有可能调用了实例的方法,导致组件业务错误
一定是接口文档给出来的接口才可以调用,否则不能使用

定义

ref    普通元素上

        组件上

ref="名称"

   <div id="app">
        <p ref="p1">元素一</p>  //可在普通元素上
        <son ref="sonref"></son>  //在需要的子组件上
        <button @click="getPEle">确定</button>
    </div>

获取

this.$refs.名称

可获取整个节点内容,通过对象.方法调用里面的方法、元素等

    <script>
        const {createApp} = Vue
        const Son = {
            data() {
                return {
                    title: '子组件',
                    count:100
                };
            },
            template:`<div>
                         <h1>{{title}}</h1>  
                     </div>`,
            methods:{
                onPlus(){
                    console.log('onplus 子组件方法');
                }
            }
        } 
        createApp({
            components:{
                Son
            },
            methods: {
                getPEle(){
                    let pEle = this.$refs.p1  //获取整个p1节点
                    console.log(pEle);  //<p>元素一</p>
                    let sonEle = this.$refs.sonref
                    console.log(sonEle.title);
                    sonEle.onPlus() //同样能调用son的方法
                    console.log(sonEle.count);
                }
            },
        }).mount('#app')

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值