vue组件通信

组件通信

父组件向子组件传递数据

  • 父发送:在使用子组件的标签上,以绑定属性的方式向子组件当中传递数据。
  • 子接收:暴露一个props属性,值为数组或对象,数组中为接收来的属性名。

语法

vParent.vue

 <!-- 在使用子组件的地方,绑定属性的方式向子组件当中传递数据-->
      <v-child a="123" :username="username" :arr="arr"></v-child>
<script>
    export default {
         data(){
            return {
                username:'张三',
                arr:['王五','李四']
            }
        }
    }
</script>

vChild.vue接收

export default {
    //使用props,此时prop为数组。
    props:['a','username','arr']
}

props验证

props为对象时,可以做props验证。

该对象中以父级传来的属性名作为属性,属性的值为对象{}。

属性的值中的属性:

  • type(类型) :常见类型:String,Number,Array,Object,Function,Boolean
  • required(必填项),值为布尔值,表示是否必传。
  • validator(验证器),函数,第一个参数为接收的值,return后是个判断,如果为true则返回接收的值。
  • default(默认值),函数需return。
<template>
  <div class="box">
      <h4>子组件</h4>
       <div>电话:{{tel}}</div>
       <div>用户名:{{username}}</div>
       <div>年龄:{{age}}</div>
  </div>
</template>

<script>
export default {
    //1. props如果是个数组是不能加验证的
    // props:[ 'tel','username','age' ]
    //2. props对象的写法可以为传递过来的数据加验证
    props:{
        tel:{
            required:true ,//代表必须传递
            validator(e){ //第一个参数为接收的值。
                return e.length == 11 //return后是个判断,如果为true则返回。
            }
        },
        username:{
            required:true ,//代表必须传递
        },
        age:{
            type:Number,
            validator(value){ //年龄必须是 1,130之间的
                return value >= 1 && value <= 130
            },
            //默认值,如果在使用此组件的时候未传age,则触发默认值
            default(){
                return 18
            }
        }
    }
}
</script>

子组件向父组件传递数据

子传父必须触发事件

  • 父发送:在使用子组件的标签上,以绑定自定义事件的方式向子组件传递自定义事件。

  • 子发送:利用props接收自定义事件,并通过this.$emit()触发自定义事件。

  • 父接收:执行自定义事件,改变自身的某些数据。

  • 父组件执行自定义事件时,传递的参数需要通过 e v e n t 来 传 递 , 但 是 event来传递,但是 event,event可以隐士传递即可省略。

语法

Vue.$emit() 触发事件

Vue.$emit(事件[,args...])//如果传递多个参数往往用对象代替

接收

<v-child  @cn="changeName($event)"></v-child>
<!--或:-->
<v-child  @cn="changeName"></v-child>

<template>
  <div class="box">
      <h4>子传父语法</h4>
    <!-- 子组件想要改变父组件的值,才会用子传父-->
    {{username}}
    {{isShow}}
    <!-- 通过绑定自定义事件把函数传给子组件 -->
    <v-child @aa="isShow=true" @cn="changeName($event)"></v-child>
  </div>
</template>

<script>

import vChild from './vChild.vue'
export default {
    components:{
        vChild
    },
    data(){
        return {
            username:'父-张三',
            isShow:false
        }
    },
    methods:{
        changeName($event){
           this.username = $event
        }
    }
}
</script>

<template>
  <div class="box">
      <h4>子组件</h4>
    <!-- 点击按钮,触发aa事件 -->
    <button @click="changeShowValue">改变父的isShow为true</button>
    <button @click="sonCn('李四')">修改name为李四</button>
  </div>
</template>

<script>

export default {
  props:['aa','cn'],
  methods:{
    changeShowValue(){
      this.$emit('aa') //触发aa事件
    },
    sonCn(name){
      this.$emit('cn',name)
    }
  }
}
</script>

子传父应用

<template>
  <div class="box">
      子传父应用-弹框
    <hr>

    <div class="login">
        <button @click="isShow1=true">登录</button>
      <!-- 这里用v-if渲染,因为在子组件里有挂载完成的方法 v-if如条件不成立则不挂载 -->
        <v-toast title="登录成功" v-if="isShow1" @hide="isShow1=false"></v-toast>
    </div>

    <div class="register">
        <button  @click="isShow2=true">注册</button> 
        <v-toast title="注册成功" v-if="isShow2" @hide="isShow2=false"></v-toast>
    </div>
    
    <div class="cart">
        <button  @click="isShow3=true">加入购物车</button> 
        <v-toast title="加入商品成功" v-if="isShow3" @hide="isShow3=false"></v-toast>
    </div>

  </div>
</template>

<script>

import vToast from './vToast.vue'

export default {
    data(){
        return {
            isShow1:false, //控制登录提示框
            isShow2:false,
            isShow3:false
        }
    },
    components:{
        vToast
    }
}
</script>

<template>
  <div class="mask">
    <span>{{title}}</span>
  </div>
</template>

<script>

export default {
  props:['hide','title'],
  mounted() {
      //这里用箭头函数:要mounted的this。
    setTimeout(()=>{
        this.$emit('hide')
    },3000)
  },
};
</script>

父传子总结

父传子,传递的数据是父变,子变;子变,父不变,还报错。

若要解决子变,父不变的问题:

  • 直接修改:传递对象。因为本质上对象是引用类型,地址不变,谁拿到地址都能修改内存中的数据。

  • 间接修改:子传父。

<template>
  <div class="box">
      基本数据类型:{{username}}
      <input type="text" v-model="username">
    <!-- 2. 传递对象。因为本质上对象是引用类型:它和的是浅拷贝 -->
        引用数据类型:{{persons.username}} 
      <input type="text" v-model="persons.username">

      <!-- 使用子组件 -->
      <v-child :username="username" :persons="persons"></v-child>
  </div>
</template>

<script>
import vChild from './vChild.vue'
export default {
    components:{
        vChild
    },
    data(){
        return {
            username:'zs',
            persons:{ username:'ls' }
        }
    }
}
</script>

<template>
  <div class="box">
      基本类型:{{username}}
      <input type="text" v-model="username">
        引用类型:{{persons.username}}
      <input type="text" v-model="persons.username">
  </div>
</template>

<script>
export default {
    props:['username','persons']
}
</script>

非父子组件通信 (eventBus)

实现非父子组件通信有三种:

  • eventBus 前提: A B组件需要同时存在。
  • 本地存储 localStorage sessionStorage
  • vuex(状态管理模式): 重点
  • cookie/session

eventBus:事件总线,子向父传递用的 $emit 底层就是基于vue中的事件总线的。

  • Vue.$on() 监听事件
Vue.$on(事件,(e)=>{//参数为接收的数据 })
  1. main.js
//Vue 基本上只在main.js中使用。而所有的.vue页面组件都继承于Vue函数

Vue.prototype.$eventBus = new Vue() //在vue原型上绑定一个Vue实例对象
//虽然组件中都有 this.$on和this.$emit,但是每一个组件都是不同的对象,因此this.$on和this.$emit并不共享。

2.要接收数据的组件,监听事件:订阅事件

//监听事件需要时时刻刻监听,因此当组件挂载完成时写监听事件
mounted(){
        this.$eventBus.$on('eventB',(e)=>{
            this.data = e
        })
    }

3.发消息的页面:触发事件可以传递数据

//把a中的数据给了b,触发事件可以传递数据,因此a触发事件。
methods:{
        sendDataToB(){
            this.$eventBus.$emit('eventB','传递的数据')
        }
    }

ref属性

  • 往html内置标签或组件身上绑定ref属性.
  • 在js中可以通过 this.$refs.属性名的属性值,访问到对应属性值的DOM节点或子组件实例.

ref操作普通DOM元素

<button ref="btn1" @click="changeBg">按钮</button>
 changeBg(){
            this.$refs.btn1.style.background = 'red'
        },

ref属性操作子组件

  • 前提是子组件成功创建。避免与v-if一起使用。
<template>
  <div class="box">
	   <!-- 在子组件上绑定ref属性,父能直接操作子中的数据或函数 -->
       <!-- 点击按钮,获取子组件中的数据 -->
      <button @click="getChildDataORFn">点击按钮,获取子组件中的数据</button>
      <v-ref ref="child"></v-ref>
</div>
</template>
<script>
export default {
    methods:{
        getChildDataORFn(){
        console.log(this.$ref.child1)//Vuecomponent实例。this.$ref.child就等于子组件里的this.
        }
    }
}
</script>

is属性(动态组件)

  • 在html内置标签上绑定is属性解决嵌套问题,即符合w3c规范。
  • 结合内置的component组件,进行组件之间切换。
  • 通过绑定标签的is属性的方式。例: 则为vlogin组件。
<template>
  <div class="box">
      <!-- 1. 解决嵌套问题 -->
      <ul>
          <!-- <v-child></v-child> -->
          <li is="v-child"></li>
      </ul>
      <table>
          <tr is='v-child'></tr>
      </table>

      <!-- 2. 结合内置的component组件,进行组件之间切换 -->
      <button @click="cname='v-login'">登录</button>
      <button @click="cname='v-register'">注册</button>
      <component :is="cname"></component>
  </div>
</template>
<script>
import vChild from './vChild.vue'
import vLogin from './vLogin.vue'
import vRegister from './vRegister.vue'
export default {
    components:{
        vChild,
        vLogin,
        vRegister
    },
    data(){
        return {
            cname:''
        }
    }
}
</script>

插槽组件(slot)

插槽组件:

<slot></slot>

匿名插槽

 <!--  匿名插槽 -->
      <v-one>
  <!-- 子组件中的内容会加入到子组件中的插槽中 -->
      <h2>有志者</h2>
      </v-one>
      <!-- 京东超市页面 -->
       <v-one>
           <a href="">回到首页</a>
       </v-one>

<template>
  <div class="box">
      匿名插槽
      <slot></slot>
  </div>
</template>

具名插槽

 <!-- 具名插槽:其实就是有名字的插槽 -->
       <v-two>
           <div slot="shang">abcd</div>
           <div slot="xia">aaaa</div>
       </v-two>

<template>
  <div class="box">
      <slot name="shang"></slot>
      <div>---------------</div>
      <slot name="xia"></slot>
  </div>
</template>

插槽作用域(重要)

指令

  • 接收子组件通过槽口传递的所有数据的方式有两种:
    slot-scope:指令(老)
    v-slot:指令(新)
  • slot-scope或者v-slot里的名字可以自定义.

原理

  • 子组件中的html标签由父级决定。
  • 父组件需要将数据(arr)传给子组件。
  • 子组件拿到数据需要加工再传给父组件,通过给slot组件(插槽)绑定属性的方式。
  • 父组件里在子组件模板中加一层tamplate模板,并通过v-slot指令获取子组件传来的数据。

<template>
  <div class="box">
<!-- 作用域插槽语法 -->
        <v-three :arr="arr">
            <!-- <template slot-scope="props"> -->
        <!-- v-slot拿到子组件数据  -->
            <template v-slot="props">
                {{props.rows}}
                <ul>
                   <li v-for="(item,index) in props.rows" :key="index">{{item}}</li>
                </ul>
            </template>
        </v-three>
  </div>
</template>
<script>
export default {
    data(){
        return {
            arr:[]
        }
    }
}
</script>

<template>
  <div class="box">
      <!-- 把数据通过插槽传给父 -->
      <slot :rows="arr2"></slot>
  </div>
</template>

<script>
export default {
    props:['arr'],
    data(){
        return {
            arr2:[]
        }
    },
    mounted(){
      this.arr2 = this.arr.map( item=>{
            return item + 1
        } )
    }
}
</script>

分析:

  <el-table :data="tableData" border style="width: 100%">
    
    <el-table-column
      prop="zip"
      label="邮编"
      width="120">
    </el-table-column>
      
    <el-table-column fixed="right" label="操作" width="100">
      <!-- 这里父组件用到了作用域插槽 ,通过scope.row获取父组件的数据 -->
      <template slot-scope="scope">  
      	<button>查看</button> 
		<button>编辑</button>
      </template>
    </el-table-column>
      
</el-table>

mixin混入

  • 操作js,以js形式存在。
  • mixin是一个对象,可以将这个对象混入到组件中。
  • 组件中的属性选项都可以在mixin对象中声明。
  • 组件引入需用mixins属性,值为数组
  • 引入的数据会添加,并且不会覆盖。

作用:
将组件中共享的数据存放在mixin中,哪个组件需要使用数据就将其引入。

1.创建mixin.js文件

export default {
    data(){
        return {
            addtime:'2021-11-11'
        }
    },
    mounted(){
        console.log('这是mixins.js中的mounted')
    }
}

2.在其它组件中引入并使用混入

<script>
// 引入混入的js文件
import mixins1 from './mixins.js'

export default {
    //放到组件中的对象中
    mixins:[ mixins1 ],//在当前组件可以使用addtime数据。
    data(){
        return {
            username:'zs'
        }
    }
}
</script>

缓存组件(keep-alive)

  • 组件keep-alive,作为缓存组件.以标签形式。
  • 使用keep-alive标签包裹的组件就是缓存组件。
  • 但是缓存组件中的组件生命周期会一直存在,因此销毁阶段不会执行。因此在缓存组件中写的事件需要用到钩子函数。

缓存组件钩子函数

  • activated 组件激活时的状态
  • deactivated 组件失活时的状态

 <template>
  <div class="box">
    缓存组件
    <button @click="falg = true">a</button>
    <button @click="falg = false">b</button>

    <!-- 保留组件进行缓存 -->
    <keep-alive>
        <v-a v-if="falg"></v-a>
        <v-b v-else></v-b>
    </keep-alive>
   
  </div>
</template>

<script>
import vA from "./vA.vue";
import vB from "./vB.vue";
export default {
  data() {
    return {
      falg: true,
    };
  },
  components: {
    vA,
    vB,
  },
};
</script>

a

<template>
  <div class="box">
      a
  </div>
</template>

<script>
export default {
    mounted(){
    console.log('a的请求');
	},
    activated(){
        // console.log('a激活了');
        console.log('a绑定了滚动事件')
    },
    deactivated(){
        // console.log('a失活了')
        console.log('要删除a的滚动事件')
    }
}
</script>

<style>

</style>

b

<template>
  <div class="box">
      B
  </div>
</template>

<script>
export default {
created(){
        console.log('b发送请求')
    }
}
</script>

<style>

</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值