组件通信(常用)- Vue.js

组件的通信

为什么要进行组件通信?
组件是一个聚合体,将来项目要合并,那么必然各个组件之间需要建立联系,这个联系就是数据通信

一、 父子通信

业务逻辑:老爸给儿子2000生活费

  1. 属性绑定 + props选项
  2. props的值
    1. 数组 props: [‘money’]
    2. 对象 props: {money:Number}
    3. 验证函数 props: {money: {validator (val) {}}}

分析:
1.老爸给儿子2000,那么数据是在Father组件身上
2.数据以属性绑定的形式给
3.Son组件通过props选项来接收绑定在身上的属性
4.数据验证

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
</head>
<body>
    <!-- 根实例 -->
    <div id="app">
        <Father/>
    </div>
    <template id='father'>
        <div>
            <!-- 跟子组件打交道的地方在这里 -->
            <!-- 以属性绑定的形式给 --> 
            <Son :money='money'/>
        </div>
    </template>
    <template id='son'>
        <div>
            <p>我一共有:{{money+n}}</p>
        </div>
    </template>
</body>
    <script>
        // 业务: 老爸有2000,给儿子
        //分析:1.老爸给儿子2000,那么数据是在Father组件身上

        //Father组件
        Vue.component('Father',{
            template:'#father',
            data(){
                return {
                    money:2000
                }
            }
        })
        //Son组件
        Vue.component('Son',{
            data(){
                return {
                    n:500
                }
            },
            template:'#son',
            //son组件通过props选项来接收绑定在身上的属性
            props:['money'] //接收到的money就相当于data中定义的数据,可以直接使用
            //属性验证
            // props:{
            //     money:Number
            // }
            //验证函数
            // props:{
            //     money:{
            //         // validator是一个验证函数,它必须有一个返回值,返回值是布尔值,可以进行正则判断
            //         validator(val){
            //             return val>3000
            //         }
            //     }
            // }
        })
        new Vue({
            el:'#app'
        })
    </script>
</html>

在这里插入图片描述
思考:但是父亲给儿子的2000元,一定是真的吗?
因此,一般我们还需要进行数据验证
1.属性验证:

//属性验证
props:{
     money:Number
 }

当Father组件中传递的数据money是字符串"2000"时,打印台会提示警告,不影响出结果,但是显示在页面上的数据会进行隐式转换,进行字符串的拼接,并不是理想得到的数据了

在这里插入图片描述
2.验证数据大小【 判断条件 】:

//验证函数
props:{
     money:{
         // validator是一个验证函数,它必须有一个返回值,返回值是布尔值,可以进行正则判断
         validator(val){
             return val>3000
         }
     }
 }

同样,打印台也会提示警告,数据2000不满足大于3000这个条件

二、2. 子父通信

业务逻辑:父亲节: 儿子给老爸888红包

  1. 通过自定义事件来完成 $emit

分析:
1.给儿子定义数据
2.钱是儿子给父亲的,Son组件要定义一个give的方法
3.私房钱改变的方法是父组件的,通过自定义事件的方式传递给子组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
</head>
<body>
    <div id="app">
        <Father/>
    </div>
    <template id="father">
        <div>
            <p>我的私房钱:{{ sifangqian }}</p>
            <!-- 自定义事件绑定的形式  相当于vm.$on('get',function () {}) -->
            <!-- 声明一个事件 -->
            <Son @get="addSifangqian"/>
        </div>
    </template>
    <template id="son">
        <div>
            <button @click='give'></button>
        </div>
    </template>
</body>
    <script>
        // 业务: 父亲节了, 儿子给老爸888红包,老爸放到私房钱里面
        Vue.component('Father',{
            data(){
                return {
                    sifangqian:500
                }
            },
            methods:{//私房钱改变的方法
                addSifangqian(val){
                    this.sifangqian += val 
                }
            },
            template:'#father',
        })
        Vue.component('Son',{
            template:'#son',
            data(){
                return {
                    hongbao:888
                }
            },
            methods:{
                give(){//给红包的方法
                    this.$emit('get',this.hongbao)//触发自定义事件
                }
            }
        })

        new Vue({
            el:'#app'
        })
    </script>
</html>

在这里插入图片描述
点击一次,由原先的500增加至1388

三、非父子组件通信

业务逻辑:姐打弟弟,弟弟哭

解决方式有两种:

  1. ref绑定 - 慎用 -> 性能不好
  2. event bus 事件总线 -> 无视层级嵌套
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
</head>
<body>
    <div id="app">
        <!-- 单项数据绑定,将父组件Root里的kick方法传给Sister组件 -->
        <Sister :kick='kick'></Sister>
         <!-- ref绑定可以获取到绑定的组件或是元素 -->
         <!-- 这里的brother就是this.$refs.brother -->
        <Brother ref='brother'></Brother>
    </div>
    <template id="sister">
        <div>
            <button @click='kick'>揍弟弟</button>
        </div>
    </template>
    <template id="brother">
        <div>
            <img v-show='flag' src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1016202020,2298260494&fm=26&gp=0.jpg" alt="">
        </div>
    </template>
</body>
    <script>
        // 业务: 姐姐打弟弟,弟弟哭起来
        //Sister组件需要拿到Brother组件上的changeFlag方法
        //解决方案:通过共有的父组件Root来做,在父组件里获取到Brother组件的一切,然后写个方法传给Sister组件
        Vue.component('Sister',{
            template:'#sister',
            props:['kick'] //props选项接受kick方法。然后直接调用
            
        })
        Vue.component('Brother',{
            template:'#brother',
            data(){
                return {
                    flag:false
                }
            },
            methods:{
                changeFlag(){
                    this.flag=!this.flag
                }
            }
        })
        new Vue({
            el:'#app',
            methods:{
                kick(){
                    console.log('this',this);
                    this.$refs.brother.changeFlag()
                }
            }
        })
    </script>
</html>

思考:Sister组件怎么才能拿到Brother组件上的changeFlag方法?
解决方案:通过共有的父组件Root来做,在父组件里获取到Brother组件的一切,然后写个方法传给Sister组件

问题:上述情况只存在一层嵌套关系,那如果是两个很多层嵌套里的子组件之间需要通信怎么解决呢?再一级一级向上找到公共的父组件?
显然不可取
解决方案:event bus 事件总线

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
</head>
<body>
  <div id="app">
    <Sister></Sister>
    <Brother></Brother>
  </div>
  <template id="sister">
    <div>
      <button @click="kick"> 揍弟弟 </button>
    </div>
  </template>
  <template id="brother">
    <div>
      <img v-show="flag" src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1016202020,2298260494&fm=26&gp=0.jpg" alt="">
    </div>
  </template>
</body>
<script>
  // 业务: 姐姐打弟弟,弟弟哭起来
  //为什么要选它作为中间人?因为eventBus里有$on和$emit两方法
  const eventBus = new Vue()
  Vue.component('Sister',{
    template: '#sister',
    methods: {
      kick () {
        eventBus.$emit('hit')//触发自定义事件
      }
    }
  })
  Vue.component('Brother',{
    template: '#brother',
    data () {
      return {
        flag: false 
      }
    },
    mounted () {
      // mounted这个选项表示,组件挂载结束,也就是说大家可以在浏览器中看到界面了
      // 虚拟dom渲染成了真实dom的时候
      eventBus.$on('hit',() => {//发布自定义事件
        this.flag = !this.flag 
      })
    }
  })
  new Vue({
    el: '#app',
  })
</script>
</html>

通过一个中间人来进行操作,就可以无视层级嵌套的问题了。
有了这种方法,是不是一下子觉得上面那种方法不香了吧

四、跨组件通信

provide & inject ( Vue3.0 新增 )
provide() 和 inject() 可以实现嵌套组件之间的数据传递。这两个函数只能在 setup() 函数中使用。父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。
1.共享普通数据
App.vue根组件:

<template>
  <div id="app">
    <h1>App 根组件</h1>
    <hr />
    <LevelOne />
  </div>
</template>

<script>
import LevelOne from './components/LevelOne'
// 1. 按需导入 provide
import { provide } from '@vue/composition-api'

export default {
  name: 'app',
  setup() {
    // 2. App 根组件作为父级组件,通过 provide 函数向子级组件共享数据(不限层级)
    //    provide('要共享的数据名称', 被共享的数据)
    provide('globalColor', 'red')
  },
  components: {
    LevelOne
  }
}
</script>

LevelOne.vue 组件:

<template>
  <div>
    <!-- 4. 通过属性绑定,为标签设置字体颜色 -->
    <h3 :style="{color: themeColor}">Level One</h3>
    <hr />
    <LevelTwo />
  </div>
</template>

<script>
import LevelTwo from './LevelTwo'
// 1. 按需导入 inject(注入)
import { inject } from '@vue/composition-api'

export default {
  setup() {
    // 2. 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
    const themeColor = inject('globalColor')

    // 3. 把接收到的共享数据 return 给 Template 使用
    return {
      themeColor
    }
  },
  components: {
    LevelTwo
  }
}
</script>

LevelTwo.vue 组件:

<template>
  <div>
    <!-- 4. 通过属性绑定,为标签设置字体颜色 -->
    <h5 :style="{color: themeColor}">Level Two</h5>
  </div>
</template>

<script>
// 1. 按需导入 inject
import { inject } from '@vue/composition-api'

export default {
  setup() {
    // 2. 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
    const themeColor = inject('globalColor')

    // 3. 把接收到的共享数据 return 给 Template 使用
    return {
      themeColor
    }
  }
}
</script>

2. 共享 ref 响应式数据
如下代码实现了点按钮切换主题颜色的功能,主要修改了 App.vue 组件中的代码,LevelOne.vue 和 LevelTwo.vue 中的代码不受任何改变:

<template>
  <div id="app">
    <h1>App 根组件</h1>

	<!-- 点击 App.vue 中的按钮,切换子组件中文字的颜色 -->
    <button @click="themeColor='red'">红色</button>
    <button @click="themeColor='blue'">蓝色</button>
    <button @click="themeColor='orange'">橘黄色</button>

    <hr />
    <LevelOne />
  </div>
</template>

<script>
import LevelOne from './components/LevelOne'
import { provide, ref } from '@vue/composition-api'

export default {
  name: 'app',
  setup() {
    // 定义 ref 响应式数据
    const themeColor = ref('red')

    // 把 ref 数据通过 provide 提供的子组件使用
    provide('globalColor', themeColor)

    // setup 中 return 数据供当前组件的 Template 使用
    return {
      themeColor
    }
  },
  components: {
    LevelOne
  }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值