Vue中通过$emit传递子组件方法至父组件时的作用域解析疑问
我在找不用refs调用子组件方法的方案时,发现了这个实现:
var Child = { template: '<div>{{value}}</div>', data: function () { return { value: 0 }; }, methods: { setValue(value) { this.value = value; } }, created() { this.$emit('handler', this.setValue); } } new Vue({ el: '#app', components: { 'my-component': Child }, methods: { setValueHandler(fn) { this.setter = fn }, click() { this.setter(70) } } })
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script> <div id="app"> <my-component @handler="setValueHandler"></my-component> <button @click="click">Click</button> </div>
子组件通过$emit把自身的setValue方法传给父组件,父组件调用这个方法就能更新子组件的状态。但我搞不懂:setValue里的this.value指向子组件的value,为什么父组件调用时,this没变成父组件作用域,反而还能更新子组件的value?
核心原因:函数的this绑定 + Vue的自动绑定处理
这本质是JavaScript的this绑定规则,再加上Vue对组件方法的特殊处理:
Vue自动绑定组件实例到methods方法
Vue在初始化组件时,会自动把methods里定义的所有方法,通过类似Function.prototype.bind的方式,把它们的this固定为当前组件实例。也就是说,子组件的setValue方法从一开始,它的this就被绑定死了——只能是子组件的实例,不管这个函数被拿到哪里去调用。传递的是已绑定this的函数引用
当子组件在created钩子中执行this.$emit('handler', this.setValue)时,传递给父组件的不是一个“原始函数”,而是已经绑定了子组件实例的函数引用。父组件把这个函数存到this.setter后调用,函数内部的this依然指向子组件实例,自然就能修改子组件的value数据。
简单说:这个函数从出生起就和子组件实例绑定了,走到哪儿都不会变。
如果是普通的未绑定函数,比如你手动写:
// 子组件里如果这么写 created() { const unboundSetValue = function(value) { this.value = value } this.$emit('handler', unboundSetValue) }
那父组件调用时this才会指向父组件(因为此时函数的this是动态绑定,由调用时的上下文决定)。但Vue的methods方法不会出现这种情况,它帮你做了自动绑定。
内容的提问来源于stack exchange,提问作者chanakya sunkarapally

