vue中的组件通信

1. 组件通信的场景

  • 父->子组件间的数据传递
  • 子->父组件间的数据传递
  • 兄弟组件间的数据传递
  • 组件深层嵌套,祖先组件与子组件间的数据传递

2. 组件通信的方式

1. 组件通信一:父子通信

父组件通过自定义属性向子组件传值
子组件通过props接收父组件的值

1.1 父->子组件传递数据的案例

html模板

<div id="app">
  <h1>父组件的内容----{{num}}h1>
	<p><button @click="change">改变父组件自己的值:{{msg}}button>p>
  <hr>
  
  <child :msg="msg" :num="num" :things-list="thingsList">child>
div>

js代码

<script src="./js/vue.js"></script>
<script>
  let Child = {
    props: ['msg', 'num', 'thingsList'],
    template: `
      

{{msg}}----{{num}}

{{thingsList}}

`
} //根组件,父组件 var vm = new Vue({ el: '#app', data: { msg: 'Happy New Year', num: [23, 45], thingsList: ['a'] }, methods: { change() { this.msg = '父组件改变的值' } }, components: { Child } }); </script>
1.2 父->子组件传递数据的注意事项
  • 每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值
  • 应该在一个子组件内部改变 prop。如果这样做了,Vue 会在浏览器的控制台中发出警告。
  • 可以先把该值赋给子组件自己的变量,然后去更改复制后的变量
  • 如果你传进来的是个对象,同时你又需要在子组件中操作传进来的这个数据,那么在父组件中的这个据也会改变,因为你传递的只是个引用
  • 你只能对对象做深拷贝创建一个副本才能继续操作
1.3. Props
1.3.1 Prop的大小写

使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

<child :msg="msg" :num="num" :things-list="thingsList">child>

在子组件内部用驼峰的方式接收

props: ['thingsList'],
1.3.2 Prop的类型

通常希望每个 prop 都有指定的值类型。

<script>
  Vue.component("Child", {
  props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    author: Object,
  },
  template: ``
})
</script>
1.3.3 Prop的验证
Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

2. 组件通信二:子父通信

2.1 子父通信

子组件传递数据给父组件是通过$emit触发自定义事件来做到的
父组件通过监听子组件发送的自定义事件来接收数据

推荐始终使用 kebab-case (短横线分隔命名)的事件名

<div id="app">
  <h1>父组件h1>
  <p>子组件传过来的值:{{info}}p>
  <hr>
  
  <child @send-data="handleSend">child>
div>

<script src="./js/vue.js">script>
<script>
  //子组件
  let Child = {
    template: `
      

`
, data() { return { msg: '子组件的数据' } }, methods: { handleClick() { //$emit向父组件发送一个自定义事件 this.$emit("send-data", this.msg) } } } var vm = new Vue({ el: '#app', data: { info: '' }, methods: { handleSend(param) { console.log(param); this.info = param } }, components: { Child } });
script>
2.2 组件中特殊的事件绑定
2.2.1 将原生事件绑定到组件

在一个组件的根元素上直接监听一个原生事件,需要使用native修饰符

<div id="app">
  
  

  
  <child @click.native="show">child>
div>

<script src="./js/vue.js">script>
<script>
  // 子组件
 let Child = {
    template: `
      

`
} // 根组件 var vm = new Vue({ el: '#app', data: {}, methods: { show() { alert(1); } }, components: { Child } });
script>
2.2.2 .sync修饰符

真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。

可以使用sync修饰符进行父子组件双向绑定

<div id="app">
  
  

  
  
  <child :msg.sync="msg">child>
  <br> {{msg}}
div>

<script src="./js/vue.js">script>
<script>
  let Child = {
    template: `

`,
    methods: {
      send() {
        // 因为子组件要修改父组件,要在属性前加update
        // update:msg为扩展的事件,目的是配合sync使用
        this.$emit("update:msg", 'child')
      }
    }
  }
  var vm = new Vue({
    el: '#app',
    data: {
      msg: 'parent'
    },
    methods: {
      getData(newVal) {
        this.msg = newVal
      }
    },
    components: {
      Child
    }
  });
script>

3. 组件通信三: 兄弟组件通信

思路: 把兄弟组件共享的数据定义在父组件,A组件通过子传父的方式,向父组件传值,父组件再通过父向子的方式,向B组件传值

4. 组件通信四:Bus总线传值

  • 适用场景 : 非父子组件传值

  • 中央事件总线(EventBus 非父子组件间通信)

    新建一个Vue事件bus对象,然后通过bus. e m i t 触 发 事 件 , b u s . emit触发事件,bus. emitbus.on监听触发的事件

  • 缺点

    EventBus通信方式是无法进行有效的组件化开发的,假设一个场景,一个页面上有多个公共组件,我们只要向其中的一个传递数据,但是每个公共组件都绑定了数据接收的方法。

css样式


html代码

<div id="app">
  <brother1>brother1>
  <brother2>brother2>
div>

<script src="./js/vue.js">script>
<script>
  Vue.component('brother1', {
    data() {
      return {
        mymessage: 'hello brother1'
      }
    },
    template: `
      

this is brother1 component!

`
, methods: { passData(val) { //触发全局事件globalEvent bus.$emit('globalEvent', val) } } }) Vue.component('brother2', { template: `

this is brother2 component!

brother1传递过来的数据:{{brothermessage}}

`
, data() { return { mymessage: 'hello brother2', brothermessage: '' } }, mounted() { //绑定全局事件globalEvent bus.$on('globalEvent', (val) => { this.brothermessage = val; }) } }) //中央事件总线 var bus = new Vue(); var app = new Vue({ el: '#app' })
script>

5. 组件通信五: Vuex

vuex算是vue中处理复杂组件通信的最佳方案,毕竟vue和vuex一个娘胎里出来的。而且vuex底层也是用vue实现的。后续会有专门的文章来讲解Vuex

6. 组件通信六: a t t r 和 attr和 attrlistener

attrs和attrs和listeners 主要用于孙组件获取父组件的属性和方法

6.1 $attrs

  • $attrs是在vue的2.40版本以上添加的
  • 项目中有多层组件传参可以使用$attrs
  • 使用普通的父子组件传参prop$emit$on会很繁琐
  • 使用vuex会大材小用,只是在这几个组件中使用,没必要使用vuex
  • 使用事件总线eventBus,使用不恰当的话,有可能会出现事件多次执行

6.2 $attrs 实现步骤

步骤1:祖先组件自定义属性
<div id="app">
  <!-- 使用子组件 -->
  <child :msg="msg" :num="num"></child>
</div>

<script src="./js/vue.js"></script>
<script>
  var vm = new Vue({
  el: '#app',
  data: {
    msg: '祖先的数据',
    num: 10
  },
  components: {
    // 注册子组件
    Child
  },
  methods: {}
});
</script>

父组件(Father.vue),给子组件关联数据,子组件如果不用props接收,那么这些数据就作为普通的HTML特性应用在子组件的根元素上

步骤2: 中间层组件添加v-bind="$attrs"
// 子组件
let Child = {
  // 注册孙组件
  components: {
    Grandson
  },
  // 使用孙组件
  template: `
  	
  `,
  inheritAttrs: true,
}

inheritAttrs: false的含义是不希望本组件的根元素继承父组件的attribute,同时父组件传过来的属性(没有被子组件的props接收的属性),也不会显示在子组件的dom元素上

步骤3: 后代组件用$attrs接受数据
  // 孙组件
  let Grandson = {
    template: `
		

`
, created() { console.log(this.$attrs); } }

6.3 $listener

l i s t e n e r s − − 属 性 , 它 是 一 个 对 象 , 里 面 包 含 了 作 用 在 这 个 组 件 上 的 所 有 监 听 器 , 你 就 可 以 配 合 ‘ v − o n = " listeners--属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 `v-on=" listenersvon="listeners"` 将所有的事件监听器指向这个组件的某个特定的子元素。

<div id="app">
  根组件A <br>
  <com-b :msg-a1="msgA1" :msg-a2="msgA2" @test1="onTest1" @test2="onTest2">com-b>
div>

<script src="./js/vue.js">script>
<script>
  let ComC = {
    template: `
			
孙组件ComC
`
, mounted() { // 孙组件直接获取A组件的属性值 console.log('c', this.$attrs); // A组件可以直接监听到C组件触发的事件 // console.log('c', this.$listeners); this.$emit("test1", 'C的数据') } } let ComB = { // C组件中能直接触发test的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 // 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) template: `
子组件ComB
`
, components: { ComC } } // 根组件A var vm = new Vue({ name: "A", el: '#app', data: { // 根组件A的数据 msgA1: 'msgA1', msgA2: 'msgA2' }, methods: { // 根组件A的事件处理程序 onTest1(data) { console.log('onTest1', data); }, onTest2() { console.log('onTest2'); } }, components: { ComB } });
script>

7.组件通信七: provide和inject

  • provide 和 inject 方式通常用于祖孙组件之间的通信,主要用于祖先组件向子组件传值

父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。

<div id="app">
  <parent>parent>
div>

<script src="./js/vue.js">script>
<script>
  Vue.component('child', {
    inject: ['for'], //得到父组件传递过来的数据
    data() {
      return {
        mymessage: this.for
      }
    },
    template: `
    	
`
}) Vue.component('parent', { template: `

this is parent compoent!

`
, // 向后代传递数据 provide: { for: '父组件的test' }, data() { return { message: 'hello' } } }) var vm = new Vue({ el: '#app', })
script>

8. 组件通信八: r e f s 、 refs、 refsparent和$children

parent和children 用于有直接父子关系 的 组件 之间的通信

  • $refs 获取子组件的实例

  • $parent 用于 获取组件的父组件实例

  • $children 用于 获取组件的子组件实例





  
  
  Document



  

父组件----{{msg}}---------------

你可能感兴趣的:(#,vue2,vue.js,javascript,前端)