#同一个路由下,父向子组件传值
props属性 -- 单向绑定
import childOne from '../components/childOne.vue';
export default{
data() {
return{
msg: '我是动态data中的数据!'
}
},
components: {
childOne
}
}
{{ info }}
{{ msg }}
export default{
props: ['info','msg']
}
父组件中
给childOne绑定一个自定义静态prop属性,这里是info
使用v-bind绑定动态prop属性,这里是msg
childOne中,在props中接收传递过来的prop属性,info、msg,可直接使用
props的具体校验规则等,详见:
#同一个路由下,子向父组件传值
使用$emit发送自定义事件
import childOne from '../components/childOne.vue';
export default{
methods:{
/* E */
sendEv(a,b,c) {
console.log(a,b,c);
}
},
components: {
childOne
}
}
按钮
export default{
created() {
//B
this.send()
},
methods:{
send() {
//A
this.$emit('s-send', '来自childOne的消息!', { name: 'Tom' }, [1,2,3,4])
}
}
}
子组件中
首先先有 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()
import childOne from '../components/childOne.vue';
import childTwo from '../components/childTwo.vue';
export default{
components: {
childOne,
childTwo
}
}
childOne
// A
import eventBus from '../eventBus.js';
export default{
// D
created() {
this.send()
},
methods:{
send() {
// B
eventBus.$emit('childOneEv', '你好啊,childTwo!')
}
}
}
// E
import eventBus from '../eventBus.js';
export default{
created() {
// F
eventBus.$on('childOneEv', res=>{
console.log(res) // 你好啊,childTwo!
})
}
}
childOne组件中
注释A:导入eventBus.js
注释B这段代码是必须的,使用$emit发射一个自定义事件并携带参数
发送时机:
可以是触发一个事件发送(注释 C)
也可以直接在生命周期中发送(注释 D)
childTwo组件中
注释E:带入eventBus.js
注释F这段是必须的,使用$on订阅发射的事件,并在回调中接收参数
接收时机:在生命周期中接收(建议在created中接收)
如果在childOne - beforeDestroy中发送事件,此时childTwo的mounted还没有触发,
但created已经触发了,所以在created接收最好。
详见:https://blog.youkuaiyun.com/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实例即可
#动态组件问题
#$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 等
#组件传值的边界情况
import childOne from '../components/childOne.vue';
export default{
data() {
return{
msg: 'sendValue'
}
},
components: {
childOne
}
}
按钮
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
}
}
}
已知上面的嵌套关系为:
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
import childOne from '../components/childOne.vue';
export default{
components: {
childOne
},
created() {
let ref = this.$refs;
console.log(ref); // B
}
}
注释A中,通过ref属性给组件绑定一个唯一名,这里是child
注释B中,通过 this.$refs 可以拿到所有的ref属性(对象格式)
$refs 只会在组件渲染完成之后生效,并且它们不是响应式的
可以使用 this.$refs.child 拿到这个组件
#使用$children获取子组件
$children 并不保证顺序,也不是响应式的
import childOne from '../components/childOne.vue';
export default{
components: {
childOne
},
created() {
let children = this.$children;
console.log(children); // A
}
}
注释A中:可以拿到所有子组件(数组)
但是顺序不能保证,可能会有异步导入的组件
#依赖注入 provide / inject 传值
首先要明确,使用依赖注入一般都是在开发插件中,因为是开发插件,所以不能使用
eventBus或vuex,因为不能确定是否用户使用了这些,若插件强行导入,会让插件
变得有侵入性,所以放弃;
$root 也不能使用,因为无法确定用户使用插件嵌套的层级
$parent 如果插件嵌套很多层,逻辑会变得更加难懂且复杂
*相当于一个加强的props,props只能父传子;但是provide/inject可以传递多个层级;
同样的缺点就是在追踪数据时,由于太深的层级,导致数据追踪困难
import childOne from '../components/childOne.vue';
export default{
// A
provide() {
return {
info: this.info,
_root: this
}
},
data() {
return {
info: '来自遥远的远方!',
msg: 'provide MSG'
}
},
components: {
childOne
}
}
{{info}}
export default{
// B
inject: ['info', '_root']
// C
inject: {
bar: {
from: 'info'
},
_root: '_root'
}
}
父组件中
注释A,使用provide导入了两个属性,info是父组件data绑定的data值;
_root导出的是this,即父组件,这是允许的
子组件中
注释B,使用inject导入需要的属性
注释C,为了防止变量污染,可以设置别名
#非props属性 $attrs
当父组件向子组件传递值时,但是子组件并没有使用props接收,此时这些属性就是非props属性,$attrs;子组件能通过 this.$attrs 拿到这些属性
attribute 会被添加到这个组件的根元素上
import childOne from '../components/childOne.vue';
export default{
components: {
childOne
}
}
export default{
inheritAttrs: false, // B
created() {
let attr = this.$attrs;
console.log(attr); // A
}
}
在子组件中,因为没有使用props绑定父组件传过来的 placeholder ,所以可以在子组件的 $attrs 中接收;
注释A:子组件中使用$attrs接收所以的非props,数据是一个对象
注释B:因为$attrs是被默认注册到子组件的根元素上的,inheritAttrs设置为false,关闭默认;
注释C:v-bind = "$attrs" ,会将所有的 $attrs 属性依次绑定到指定的元素上。
# $listeners