跳至主要內容

组件通信

哓番茄大约 4 分钟

组件通信

父子组件

父子组件通信主要是props/emitparent/childrenref

props/$emit

父组件中,通过给子组件标签v-bind绑定属性的方式传入值

<ComponentName v-bind:name="value"></ComponentName>

如果不使用v-bind传入的值为字符串,使用v-bind绑定传入的值为表达式。

子组件中,通过props对象接收值

 props: {
    name: { // 接收父组件传入值
        type: String || ...,
        default: ''
    }
 }

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

子组件不能直接修改父组件传入的值

这里有两种常见的试图改变一个 prop 的情形:

  1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}
  1. 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

子组件传值给父组件

APIopen in new window

子组件通过$emit派发事件和值给父组件(值可以有多个)

this.$emit('fnX', value)

父组件通过v-on绑定子组件派发的事件,并触发一个新的事件,新的事件内可以接收传来的值

<ComponentName @fnX="fnY"></ComponentName>

methods: {
	fnY(value) {
		console.log(value)
	}
}

父组件调用子组件方法传入值

通过ref引用调用子组件内的方法并传入参数

父组件:

<子组件标签  ref="refName"></子组件标签>

methods: {
    fnX(x) {
      this.$refs.refName.fnY(x) // 调用子组件方法并传入值
    }
}

子组件:

methods: {
    fnY(x) {
      this.x = x
    }
  }
}

向子|孙组件传值

向子孙传值主要是用attrs/$listenersprovide/inject APIVuex

attrs/listeners

用在父组件传递数据给子组件或者孙组件,$attrs继承所有的父组件属性(除了prop传递的属性、classstyle

当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

$listeners是一个对象,包含了父作用域中的v-on事件监听器,可以配合v-on="$listeners"将所有的事件监听器指向这个组件的某个特定的子元素

provide/inject

祖先组件中通过provider来提供变量,然后在孙组件中通过inject来注入变量

procide/inject API主要解决了跨域组件间的通讯问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系

非父子组件间传值

子组件1中把值传到父组件,父组件获取值传入子组件2

父组件:

<子组件1 @方法名x="方法名y"></子组件1>
<子组件2 :值名称x="值x"></子组件2 >
data() {
	return {
	 值x: ''
	}
},
methods: {
	方法名y() {
		this.值x =}
}

子组件1:

this.$emit('方法名x',) // 传出值

子组件2:

props: {
    值名称x: { // 接收父组件传入值
        type: String,
        default: ''
    }
}

当组件的嵌套多时,非父子组件间传值就显得复杂,除了上面的方法和使用vuexopen in new window实现之外,还可以通过eventBus(或者叫 总线/发布订阅模式/观察者模式)的方式实现非父子组件间传值。

<div id="root">
    <child1 content="组件1:点我传出值"></child1>
    <child2 content="组件2"></child2>
</div>

<script type="text/javascript">
	Vue.prototype.bus = new Vue()
	// 每个Vue原型上都会有bus属性,而且指向同一个Vue实例
	Vue.component('child1', {
		props: {
			content: String
		},
		template: '<button @click="handleClick">{{content}}</button>',
		methods: {
			handleClick(){
				this.bus.$emit('change', '我是组件1过来的~') // 触发change事件,传出值
			}
		}
	})

	Vue.component('child2', {
		data() {
			return {
				childVal: ''
			}
		},
		props: {
			content: String,
		},
		template: '<button>{{content}} + {{childVal}}</button>',
		mounted() {
			this.bus.$on('change', (msg) => { // 绑定change事件,执行函数接收值
				this.childVal = msg
			})
		}
	})

	var vm = new Vue({
		el: '#root'
	})
</script>

上面代码中,在Vue原型上绑定一个eventbus属性,指向一个Vue实例,之后每个Vue实例都会有一个eventbus属性。

此方法传值,不限于兄弟组件之间,其他关系组件间都适用。