Vue 组件之间传值

#同一个路由下,父向子组件传值

props属性 -- 单向绑定

<!-- 父组件 -->
<template>
  <div>
    <child-one info='我是你的父组件!' v-bind:msg='msg' />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
    data() {
      return{
        msg: '我是动态data中的数据!'
      }
    },
    components: {
      childOne
    }
  }
</script>
<!-- childOne 子组件 -->
<template>
  <div>
    <p> {{ info }} </p>
    <p> {{ msg }} </p>
  </div>  
</template>
<script>
  export default{
    props: ['info','msg']
  }
</script>
  • 父组件中
    • 给childOne绑定一个自定义静态prop属性,这里是info
    • 使用v-bind绑定动态prop属性,这里是msg
  • childOne中,在props中接收传递过来的prop属性,info、msg,可直接使用

props的具体校验规则等,详见:

https://cn.vuejs.org/v2/guide/components-props.html



#同一个路由下,子向父组件传值

使用$emit发送自定义事件

<!-- 父组件 -->
<template>
  <div>
    <!-- D -->
    <child-one @s-send='sendEv'/>
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
  methods:{
    /* E */
    sendEv(a,b,c) {
      console.log(a,b,c);
    }
  },
    components: {
      childOne
    }
  }
</script>
<!-- childOne 子组件 -->
<template>
  <div>
    <!-- C -->
    <button @click="send">按钮</button>
  </div>  
</template>
<script>
  export default{
    created() {
      //B
      this.send()
    },
    methods:{
      send() {
        //A
        this.$emit('s-send', '来自childOne的消息!', { name: 'Tom' }, [1,2,3,4])
      }
    }
  }
</script>
  • 子组件中

    • 首先先有 A ,this.$emit() 这一段代码
    • 何时发给父组件(任选)
      • ① 注释B:在子组件生命周期中发送
      • ② 注释C:在DOM中写一个事件,触发这个事件时发送
  • 父组件中

    • 注释D:使用子组件$emit的自定义事件绑定一个自己的事件,这个是必须的
    • 注释E:methods中的事件参数,可以获取来自子组件传的值

//$emit的使用
this.$emit()
@params
  参1:自定义事件名
  参2 ~ 参n:传递的参数,支持Number、String、Object、Array、Function、this 等


#同一路由下,非父子之间传值

可以是兄弟之间、爷孙之间、或者是父子之间,总之都可以使用!
事件总线:eventBus

下面演示兄弟之间传值,childOne与childTwo是一对兄弟组件;
childOne向childTwo传值

/* 首先先创建一个eventBus.js */
import Vue from 'vue';
export default new Vue()
<!-- 父组件 -->
<template>
  <div>
    <child-one />
    <child-two />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  import childTwo from '../components/childTwo.vue';
  export default{
    components: {
      childOne,
      childTwo
    }
  }
</script>

<!-- childOne 子组件 -->
<template>
  <div>
    <!-- C -->
    <button @click="send">childOne</button>
  </div>  
</template>
<script>
  // A
  import eventBus from '../eventBus.js';
  export default{
    // D
    created() {
      this.send()
    },
    methods:{
      send() {
        // B
        eventBus.$emit('childOneEv', '你好啊,childTwo!')
      }
    }
  }
</script>
<!-- childTwo 子组件 -->
<template>
  <div>
    
  </div>  
</template>
<script>
  // E
  import eventBus from '../eventBus.js';
  export default{
    created() {
      // F
      eventBus.$on('childOneEv', res=>{
        console.log(res) // 你好啊,childTwo!
      })
    }
  }
</script>
  • childOne组件中
    • 注释A:导入eventBus.js
    • 注释B这段代码是必须的,使用$emit发射一个自定义事件并携带参数
    • 发送时机:
      • 可以是触发一个事件发送(注释 C)
      • 也可以直接在生命周期中发送(注释 D)
  • childTwo组件中
    • 注释E:带入eventBus.js
    • 注释F这段是必须的,使用$on订阅发射的事件,并在回调中接收参数
    • 接收时机:在生命周期中接收(建议在created中接收)

    如果在childOne - beforeDestroy中发送事件,此时childTwo的mounted还没有触发,
    但created已经触发了,所以在created接收最好。
    详见:https://blog.csdn.net/m0_37508531/article/details/103847541


#关于eventBus的另一种写法
/* eventBus.js */
import Vue from 'vue';
let bus = new Vue();

//直接挂在在Vue原型上
Vue.prototype.bus = bus;

//然后在childOne、childTwo中
//就可以不用导入eventBus.js了
//eventBus.$emit() 写成 this.bus.$emit() 即可

/* eventBus.js */
import Vue from 'vue';
export default new Vue()

//如果你想更严谨一点,可以导入到main.js中去挂载

/* main.js */
import bus from '../eventBus.js';
Vue.prototype.bus = bus;

//然后在childOne、childTwo中
//就可以不用导入eventBus.js了
//eventBus.$emit() 写成 this.bus.$emit() 即可

#eventBus的原理
  • 事件总线的原理其实就是发布订阅模式
  • 能够实现的原理就是需要有两个方法,$on:订阅 $emit:发布
  • 而Vue实例本身就是提供了这两个方法,所以直接使用Vue实例即可
#动态组件问题

这个没有总结,详见:https://www.cnblogs.com/ljh-dream/p/10048291.html

#$emit 和 $on
//$emit的使用
this.$emit()
@params
  参1:自定义事件名(用于传递给$on)
  参2 ~ 参n:传递给$on的参数,支持Number、String、Object、Array、Function、this 等


//$on的使用
this.$on()
@params
  参1:自定义事件名(用于接收$emit的事件)
  参2 ~ 参n:用于接收$emitn的参数,支持Number、String、Object、Array、Function、this 等



#组件传值的边界情况

首先边界情况的传值均不常用
详见:https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E8%AE%BF%E9%97%AE%E5%85%83%E7%B4%A0-amp-%E7%BB%84%E4%BB%B6

<!-- 父组件 sendValue.vue -->
<template>
  <div>
    <child-one />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
    data() {
      return{
        msg: 'sendValue'
      }
    },
    components: {
      childOne
    }
  }
</script>
<!-- childOne 子组件 -->
<template>
  <div>
    <button @click="handle">按钮</button>
  </div>  
</template>
<script>
  export default{
   methods:{
    handle() {
     let root = this.$root;
     let parent = this.$parent;
     console.log(root); // A
     console.log(parent);// B
     console.log(root == parent); // false
    }
   }
  }
</script>

已知上面的嵌套关系为:
App.vue --> sednValue.vue --> childOne.vue

在childOne.vue组件中的 $root和 $parent
  • 注释A的 $root 其实是App.vue

    • 在main.js中new Vue()绑定的el的DOM(html)为根实例
    • 通过render函数又把App.vue替换了el绑定的DOM
    • 所以App.vue成了唯一的根实例 $root
  • 注释B的 $parent 是他的父组件sendValue.vue



#父组件中通过ref属性拿到子组件

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs

详见:https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E8%AE%BF%E9%97%AE%E5%AD%90%E7%BB%84%E4%BB%B6%E5%AE%9E%E4%BE%8B%E6%88%96%E5%AD%90%E5%85%83%E7%B4%A0

<!-- 父组件 -->
<template>
  <div>
    <!-- A -->
    <child-one ref='child' />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
    components: {
      childOne
    },
    created() {
      let ref = this.$refs;
      console.log(ref); // B
    }
  }
</script>
  • 注释A中,通过ref属性给组件绑定一个唯一名,这里是child
  • 注释B中,通过 this.$refs 可以拿到所有的ref属性(对象格式)
    • $refs 只会在组件渲染完成之后生效,并且它们不是响应式的
    • 可以使用 this.$refs.child 拿到这个组件


#使用$children获取子组件

$children 并不保证顺序,也不是响应式的

详见:https://cn.vuejs.org/v2/api/#vm-children

<!-- 父组件 -->
<template>
  <div>
    <child-one />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
    components: {
      childOne
    },
    created() {
      let children = this.$children;
      console.log(children); // A
    }
  }
</script>
  • 注释A中:可以拿到所有子组件(数组)
    • 但是顺序不能保证,可能会有异步导入的组件


#依赖注入 provide / inject 传值

首先要明确,使用依赖注入一般都是在开发插件中,因为是开发插件,所以不能使用
eventBus或vuex,因为不能确定是否用户使用了这些,若插件强行导入,会让插件
变得有侵入性,所以放弃;

$root 也不能使用,因为无法确定用户使用插件嵌套的层级

$parent 如果插件嵌套很多层,逻辑会变得更加难懂且复杂

详见:https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5

*相当于一个加强的props,props只能父传子;但是provide/inject可以传递多个层级;
同样的缺点就是在追踪数据时,由于太深的层级,导致数据追踪困难

<!-- 父组件 -->
<template>
  <div>
    <child-one />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
    // A
    provide() {
      return {
        info: this.info,
        _root: this
      }
    },
    data() {
      return {
        info: '来自遥远的远方!',
        msg: 'provide MSG'
      }
    },
    components: {
      childOne
    }
  }
</script>
<!-- childOne 子组件 -->
<template>
  <div>
    <button>{{info}}</button>
  </div>  
</template>
<script>
  export default{
   // B
   inject: ['info', '_root']

   // C
   inject: {
     bar: {
       from: 'info'
     },
     _root: '_root'
   }
  }
</script>
  • 父组件中

    • 注释A,使用provide导入了两个属性,info是父组件data绑定的data值;
      _root导出的是this,即父组件,这是允许的
  • 子组件中

    • 注释B,使用inject导入需要的属性
    • 注释C,为了防止变量污染,可以设置别名


#非props属性 $attrs
  • 当父组件向子组件传递值时,但是子组件并没有使用props接收,此时这些属性就是非props属性,$attrs;子组件能通过 this.$attrs 拿到这些属性
  • attribute 会被添加到这个组件的根元素上

详见:https://cn.vuejs.org/v2/guide/components-props.html#%E9%9D%9E-Prop-%E7%9A%84-Attribute

<!-- 父组件 -->
<template>
  <div>
    <child-one placeholder='我是测试attr属性!' title='input标签!' />
  </div>  
</template>
<script>
  import childOne from '../components/childOne.vue';
  export default{
    components: {
      childOne
    }
  }
</script>
<!-- childOne 子组件 -->
<template>
  <div>
    <!-- C -->
    <input type="text" v-bind="$attrs" />
  </div>  
</template>
<script>
  export default{
    inheritAttrs: false, // B
    created() {      
      let attr = this.$attrs;
      console.log(attr); // A
    }
  }
</script>

在子组件中,因为没有使用props绑定父组件传过来的 placeholder ,所以可以在子组件的 $attrs 中接收;

  • 注释A:子组件中使用$attrs接收所以的非props,数据是一个对象
  • 注释B:因为$attrs是被默认注册到子组件的根元素上的,inheritAttrs设置为false,关闭默认;
  • 注释C:v-bind = "$attrs" ,会将所有的 $attrs 属性依次绑定到指定的元素上。
<!-- 子组件的input经过v-bind='$attrs',渲染出来的DOM显示如下:  -->
<input type="text" placeholder="我是测试attr属性!" title="input标签!">

<!-- 
  v-bind="{ title:'你好', age: 99 }"
    等价于
  v-bind:title = "'你好'"  v-bind:age = "99"
 -->
<!-- 代码 -->
<p v-bind="{ title:'你好', age: 99 }"></p>
<!-- 渲染出来的DOM -->
<p title="你好" age="99"></p>



# $listeners

https://cn.vuejs.org/v2/api/#vm-listeners

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 自定义组件:父组件中使用v-model将值传入子组件,并且子组件也能将值传回父组件,v-model是双向传递。 而...
    封_绝阅读 2,493评论 0 0
  • 前言组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引...
    翔ni阅读 214评论 0 0
  • 什么是函数式组件? 函数式组件就是函数是组件,组件是函数,它的特征是没有内部状态、没有生命周期钩子函数、没有thi...
    microkof阅读 6,022评论 2 8
  • 前言 props实现父传子 codepen: 我的在线实例[https://codesandbox.io/s/co...
    buuoltwo阅读 378评论 0 0
  • 一、Vue官方文档 https://cn.vuejs.org/v2/guide/components.html#%...
    Lia代码猪崽阅读 7,167评论 0 1