#同一个路由下,父向子组件传值
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
<!-- 父组件 -->
<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,即父组件,这是允许的
- 注释A,使用provide导入了两个属性,info是父组件data绑定的data值;
-
子组件中
- 注释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>