【Vue】组件通信组件通信-LMLPHP

       📝个人主页:五敷有你      

 🔥系列专栏:JVM

⛺️稳中求进,晒太阳

【Vue】组件通信组件通信-LMLPHP

组件通信

组件通信,就是指组件与组件之间的数据传递

  • 组件的数据是独立的,无法直接访问其他组件的数据
  • 想用其他组件的数据-->组件通信

组件关系:

  • 父子关系

    • props和$emit
      • prop定义:组件上定义的属性
      • prop作用:向子组件传递数据
      • 特点:
        • 可以传递任何数量的prop
        • 可以传递任何类型的prop
      • props的校验
        • 为组件的prop指定验证要求,不符合要求,控制台就会有错误显示
        • 语法:
        • 类型校验
        • 非空校验
        • 默认值
        • 自定义校验
//类型校验
props:{
    检验的属性名:类型
}
//全
props:{
    校验的属性名:{
        type:类型,
        require:true,
        default:"默认值",
        validator(value){
                    //自定义校验逻辑
                    return 是否通过校验
        }    
    }
}
  • 父传子

【Vue】组件通信组件通信-LMLPHP

  • 子传父

【Vue】组件通信组件通信-LMLPHP

  • 非父子关系

    • provide和inject
    • eventbus
  • 通用解决方案:Vuex(适合复杂业务场景)

小黑记事本(组件化版)

【Vue】组件通信组件通信-LMLPHP

App.vue

<template>
  <div id="app">
    <div class="main">
      <TodoHeader @addItem="add" ></TodoHeader>
      <TodoMain :list="list" @deleteItem="del"></TodoMain>
     <TodoFooter :totalNum="totalNum" @clearItem="clear"></TodoFooter>
    </div>
  </div>
</template>

<script>
import TodoHeader from './components/TodoHeader.vue';
import TodoMain from './components/TodoMain.vue';
import TodoFooter from './components/TodoFooter.vue';

export default {

  name: 'App',
  components: {
   TodoHeader,
   TodoMain,
   TodoFooter
  },
  data(){
    
     return {

      list:JSON.parse(localStorage.getItem("list"))||[
        {id:1,name:"打篮球"},
        {id:2,name:"打足球"},
        {id:3,name:"打排球"},
        {id:4,name:"打气球"}],
     }
  },
  computed:{
    totalNum(){
      return this.list.length
    }
  },
  watch:{
      list:{
        deep:true,
        handler(newValue){
            localStorage.setItem("list",JSON.stringify(newValue))
        }
      }
  },
  methods:{
    del(id){
      this.list=this.list.filter(item=>item.id!==id)
    },
    clear(){
        this.list=[]
    },
    add(todoName){
      this.list.unshift({
        id:+new Date(),
        name:todoName
      })
    }
  }
}
</script>

<style scoped>
#app {
  height: 1200px;
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;

  margin-top: 60px;
  display: flex;
  justify-content: space-around;
}
.main{
  width: 40%;
 

}
</style>

TodoHead.vue

<template>
    <div class="main">
        <div class="title">记事本</div>
        <div class="search">
            <input type="text" v-model.trim="item" v v-on:keyup.enter="addList"><input type="button" @click="addList" value="添加">
        </div>
    </div>
</template>
<script>

export default {
    data(){
        return {
            item:""
        }
    },
    methods:{
        addList(){
            this.$emit("addItem",this.item)
            this.item=""
        }
    }
}
</script>
<style scoped>
.main{
    width: 100%;

}
</style>

TodoMain.vue

<template>
    <div class="main">
        <ul class="todo-list">
            <li v-for="(item,index) in list" :key="item.id" >
              <div><span>{{ index+1 }}</span> &nbsp;<span>{{ item.name }}</span> </div> <div><button @click="handlerDelete(item.id)">X</button></div>
            </li>
        </ul>
    </div>
</template>
<script>

export default {
    props:{
        list:Array
    },
    methods:{
    handlerDelete(id){
        this.$emit("deleteItem",id)
    },
    }
}
</script>
<style scoped>
    .main{
        width: 100%;
    }
    li{
        padding: 5px 2px;
        border-bottom: 1px solid gray;
        list-style: none;
        display: flex;
        justify-content: space-around;

    }
</style>

TodoFooter.vue

<template>
    <div class="main">
        <span>{{totalNum}}</span> <span @click="handerClear">清空记录</span>
    </div>
</template>
<script>

export default {
props:{
    totalNum:Number
},
    methods:{
        handerClear(){
            this.$emit("clearItem")
        }
    }
    
}
</script>
<style scoped>
.main{
    display: flex;
    justify-content: space-between;

}
span{
    color: gray;
    font-size: 13px;
    padding: 10px;
}
</style>

非父子间通信(两种方式)

非父子通信(enent bus)

作用:

非父子组件之间,进行简易的消息传递(复杂场景→Vuex)

步骤:
  1. 创建一个都能访问到的事件总线(空的VUe实例)→utils

文件名:EventBus.js

创建一个都能访问到的事件总线(空的Vue实例)
import Vue from 'vue'

const Bus=new Vue()

export default Bus

        2. A组件(接收方),监听Bus实例的事件

<script>
import Bus from '../utils/EventBus'
export default {
    data(){
        return {
            msg:""
        }
    },
    created(){
        //2.在A组件进行bus的事件监听
        Bus.$on("sendMsg",(msg)=>{
            this.msg=msg
        })
    }
  

}
</script>

        3. B组件(发送方),触发Bus实例的事件

import Bus from '../utils/EventBus'
export default {
    methods:{
        clickSend(){
            //3.B组件触发事件的方式传递参数
            Bus.$emit("sendMsg","今天晴天,适合出去玩")
        }
    }
}
</script>

非父子通信--provide&inject

provide & inject 作用:跨层级共享数据

  1. 父组件provide提供数据
export default{
    provide(){
        //普通类型【非响应式】
        color:this.color,
        //复杂类型【响应式】
        userInfo:this.userInfo    
    }
}

       2. 子/孙组件inject取值使用

export default{
    inject:['color','userInfo'],
    created(){
        console.log(this.color,this.userInfo)    
    }
}
02-27 02:56