vue组件通信

组件通信 -- 父传子通信

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <son father-msg="message"></son>
  </div>
</body>
</html>
<script>
let Son = {
  template: '<p>{{FatherMsg}}</p>',
  props: {
    FatherMsg: {
      type: String,
      default: '',
      required: true
    }
  },
  mounted () {
    console.log(this.FatherMsg)
  }
}
let app = new Vue({
  el: '#app',
  components: {
    'son': Son
  },
  data: {
    message: '组件通信-父传子'
  }
})
</script>

这个就是初始的页面代码
父组件

<div id='app'></div>

子组件

<son father-msg='message'></son>

父组件向子组件传递父组件里面写入的数据的关键就是:propsprops可以是数组,数组是简写模式,只写入用来接收父组件传入信息的标识符。也可以是对象,对象是完整写法,可以用来规定子组件用来接收父组件传过来的数据的标识符的数据类型(如:String,Number等),默认值,是否必须等。
数组模式

props: ['flag1','flag2',...,'flagn']

对象模式

props: {
    flag1: {
        type: String,
        default: '',
        required: true
    },
    flag2: {
        type: Number,
        default: 0,
        required: false,
        say: function (val) {
            console.log(val)
        }
    }
}

Tips:我的demo里面props里面的标识符用的是驼峰命名法:FatherMsg,但是在组件标签里面却是全部小写并且用连接符“-”连接,这里要注意,不论是组件名,还是props的标识符,不论是怎么写的,在组件里面一定要全部小写,并用连字符"-"连接

通过子组件的mounted生命周期钩子函数打印出来的结果是:message,就会发现,通过标识符,我们可以在子组件获取到上面写入的数据

但这并不是我们的目的,我们的目的是获取到父组件data里面的message,所以我们需要再借助vue的另一个指令:v-bind(缩写就是"😊

<div id="app">
    <son v-bind:father-msg="message"></son>
    // 或者
    <son :father-msg="message"></son>
</div>

现在打印输出一下结果:

组件通信 -- 子传父通信

我们需要修改一下HTML代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    {{sonTxt}}
    <son :father-msg="message" v-on:sonsend="sonsend"></son>
  </div>
</body>
</html>
<script>
let Son = {
  template: '<div id="son"><p>{{FatherMsg}}</p><button @click="handleSend">点击传递子组件信息</button></div>',
  props: {
    FatherMsg: {
      type: String,
      default: '',
      required: true
    }
  },
  data: function () {
    return {
      word: '这是子组件的信息'
    }
  },
  methods: {
    // 通过出发这个函数想父组件传递数据
    handleSend() {
      // `sonsend`是标识符,也是父组件用来通过`v-on(@)`绑定的事件方法,里面的参数就是子组件传递过去的数据
      this.$emit('sonsend',this.word)
    }
  },
  mounted () {
    console.log(this.FatherMsg)
  }
}
let app = new Vue({
  el: '#app',
  components: {
    'son': Son
  },
  data: {
    message: '组件通信-父传子',
    sonTxt: ''
  },
  methods: {
    // 绑定在子组件上面的函数方法
    sonsend (val) {
      console.log(val)
      this.sonTxt = val
    }
  }
})
</script>

子组件向父组件传递自己内部的一些数据,关键就是:this.$emit,在子组件中通过绑定一个点击事件通过this.$emit向父组件传递数据,this.$emit()方法有两个参数,第一个参数是子组件传递数据的标识符,也是父组件用来接收子组件数据的方法名,第二个参数就是子组件用来传递的数据
子组件一定要通过事件触发发射器,向父组件发送数据

methods: {
   // 通过出发这个函数想父组件传递数据
   handleSend() {
     // sonsend是标识符,也是父组件用来通过v-on(@)绑定的事件方法名,里面的参数就是子组件传递过去的数据
     this.$emit('sonsend',this.word)
  }
}  

父组件里面,一定要在子组件上绑定一个同子组件标识符一样的函数

<son :father-msg="message" v-on:sonsend="sonsend"></son>

通过点击子组件的按钮,我们发现父组件的sonTxt由空字符串变成了子组件里面的word

组件通信 -- 非父子通信

非父子通信跟子传父通信逻辑代码差不多,但是因为两者没有关联系,就像相亲,两个人八竿子打不着,所以怎么可能有交集呢?这个时候就需要一个我们常说的媒婆来作为中间人,来给两者拉线搭桥,同理,非父子因为没有关联性,所以我们需要一个中间件来勾连两个组件的之间的关系,使他们通过这个中间件实现联系。

一、中间件

在utils文件夹中创建一个bus.js文件,里面的代码如下:

import Vue from 'vue'
export default new Vue()

二、全局API

或者在main.js文件中,或者html文件中,在vue的原型上挂载一个方法

// main.js
Vue.prototype.$eventBus = new Vue()

// html文件
let $eventBus = new Vue()

然后即可在要传递数据的组建中通过$emit发射传递数据,第一个参数是定义的接受标识符,也是另外一个组件接受数据的方法名,第二个参数为要传递的值

如果是通过bus.js创建的中间件,则需要在要传递数据的组件中引入这个中间件,类似node引入中间件

import $eventBus from './utils/bus.js'

通过在该组件中触发函数,调用发射器方法,想其他组件发射数据

现在我们先通过Vue.component()创建一个全局组件,这个组件就不是Vue实例的子组件,不需要在vue实例的components中注入即可使用
TipsVue.component()必须写在vue实例之前,否则没有效果

Vue.component('Other',{
  template: '<div id="other"><p>这是一个非父子组件</p><button @click="handleEmit">点击向其他组件传递数据</button></div>',
  data: function () {
    return {
      otherMessage: '这是一个非父子组件传递过来的数据'
    }
  },
  methods: {
    handleEmit () {
       // 第一个参数为发送数据的标识符,也是其他组件监听接收数据的方法名,第二个参数就是需要传递的数据
      $eventBus.$emit('othersend', this.otherMessage)
    }
  }
})

声明中间件:因为这个本身就是在当前页面使用的,所以不需要this.$eventBus

let $eventBus = new Vue()

现在我们已经在全局组件中绑定了函数触发发射器,现在就需要在接收数据的组件中通过this.$eventBus.$on()方法来接收数据,这个方法有两个参数,第一个参数是接收数据的方法名,第二个参数是一个回调函数,这个回调函数也有一个参数,这个参数就是其他组件发送过来的数据,记住必须在mounted()生命周期钩子函数里面调用,因为非父子通信的前提就是两个组件都已经渲染在页面

mounted () {
    $eventBus.$on('othersend', function (val) {
      console.log(val)
      this.otherTxt = val
    })
  }

现在我们来看一下效果:

没有任何数据,和效果,现在我们点击按钮再看一下:

效果出来了,但是有一个奇怪的现象就是不知道为什么页面没有渲染这个数据?如果有大佬看见这个笔记,可以帮忙指点一下
HTML代码:

<div id="app">
   {{sonTxt}}
   <son :father-msg="message" v-on:sonsend="sonsend"></son>
   {{otherTxt}}
   <other></other>
</div>

但其实数据是传过来并改变了的,不知道为什么没有效果,我们可以通过监听器来监听:

watch: {
    otherTxt: function (newVal, oldVal) {
      console.log('没传值之前的otherTxt是:' + oldVal +'--传值之后otherTxt是:' + newVal)
    }
  }

再来看下打印结果:

填坑:

做到这里,我们发现之前有一个坑没有填,那就是非父子组件通信,只改变了,但是视图数据没有实时更新,其实不管是非父子还是其他组件通信,都有可能遇到这个问题,其实原因就是,因为为了方便一些像我一样的小白看懂,所以采用了普通函数的写法,并没有使用箭头函数,所以导致函数里面的this指向的是window,而不是Vue实例,如果改成箭头函数,或者重新赋值一下this指向,就可以了。

mounted () {
    let that = this
    $eventBus.$on('othersend', function (val) {
      console.log(val)
      that.otherTxt = val
    })
  },
  watch: {
    otherTxt: function (newVal, oldVal) {
      console.log('没传值之前的otherTxt是:' + oldVal +'--传值之后otherTxt是:' + newVal)
    }
  }

如上面代码,我们已经修改了this指向,现在我们再来打印一下结果看看:

这样组件通信算是完美解决了!

posted @ 2019-03-29 18:48  不会代码的前端  阅读(190)  评论(0编辑  收藏  举报