深入父子组件传值

前言

之前学习vue,对父子组件传值达成了基本的认识,现在我把它进行系统的整理

父组件向子组件传值prop

首先在父组件中

<son :msg="我是你爸爸"></son>

在子组件的props中

export default {
    props:{
		'msg':String
	}
}

这里我还加了一个数据类型的检测,注意,数据类型是在组件实例创建之前进行验证的

子组件向父组件传值$emit

子组件中,需要触发一个事件,可以是自定义事件,也可以是原生事件,在这里我们先用自定义事件,原生事件比较复杂,我放在后面说

首先我们在子组件中用$emit触发自定义事件,这个自定义事件写在生命周期函数或者是click事件上均可,然后第二个参数是我们要传给父组件的值

this.$emit('hellodad','爸爸我很想你');

然后我们在父组件中监听子组件的自定义事件,并在监听到的时候,触发happy事件

<son v-on:hellodad="happy"></son>
export default {
    methods:{
      happy(data){
        console.log(data);
        console.log('hello,my son');
      }
    },
    components:{
      son
    }
}

data中就是子组件中打印出来的消息啦。
在这里插入图片描述

vm.$attrs

那么什么是$attrs呢,下面是官方定义

包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

父亲给儿子传了三个属性

<son
	msg1:'1'
	msg2:'2'
	msg3:'3'
></son>

儿子说,我不要那么多,要一个就好了,剩下给你孙子吧

<grandson v-bind="$attrs"></grandson>
export default {
	props:['msg1']
}

孙子说,气死了,你怎么尽挑不要的给我

created(){
	console.log(this.$attrs);//{v-bind:{msg2:'2',msg3:'3'}}
}

inheritAttrs

官方解释

1.默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。2.而通过 (同样是 2.4 新增的) 实例属性 $attrs 可以让这些特性生效,且可以通过 v-bind 显性的绑定到非根元素上
注意:这个选项不影响 class 和 style 绑定。

首先,我们来理解一下第一句话

在父元素中设置

<son msg="son" class="son"></son>

在子元素中不设置props进行接收

export default {
	inheritAttrs:false
}

在这里插入图片描述
父组件给子组件设置的值就会退化成为子组件的属性

在子组件中设置

exportt default{
	inheritAttrs:false
}

在这里插入图片描述
看,msg属性消失了,它不会退化为子组件普通的 HTML 特性了,但是classstyle则不会。这是因为,在父组件中不被认作 props 的特性绑定会覆盖子组件根组件上的同名特性,而classstyle则会发生合并。

2.然后,我们来理解一下第二句话,因为有$attrs,我们用祖孙三辈组件来进行解释

父亲

<son
	msg1:'1'
	msg2:'2'
	msg3:'3'
></son>

儿子

<grandson v-bind="$attrs"></grandson>
export default {
	props:['msg1']
}

可以看到,这些特性生效,且可以通过 v-bind 显性的绑定到元素上。至于是不是根元素,其实主要是看你的写法,如果你是直接在子组件的components中定义孙子元素,并且把$attrs绑定在孙子上面,那理解为非根元素也无可厚非。
在这里插入图片描述

.native

使用 v-on 的.native 修饰符,可以在直接监听组件的根元素的一个原生事件。

<base-input v-on:focus.native="onFocus"></base-input>

我在给router-link绑定点击事件的时候,就需要使用.native修饰符,否则点击事件不会生效。因为router-link是一个自定义标签,他没有自己的事件和方法,所以我们用.native监听这个组件根元素的原生事件,就可以触发点击事件了。
可是有的时候,在你尝试监听一个类似 的非常特定的元素时,这并不是个好主意。比如上述 组件可能做了如下重构,所以根元素实际上是一个

<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  >
</label>

这时,父级的 .native 监听器将静默失败。它不会产生任何报错,但是 onFocus 处理函数不会如你预期地被调用。

$listeners

为了解决这个问题,Vue 提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。例如:

{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

下面是$listeners的官方解释

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

啥意思呢?我们还是用祖孙三辈举例
父亲和儿子说,请把孙子今天的表现告诉我

<son @actiom="happy"></son>

儿子:帮忙父亲监视孙子

<grandson v-on="$listeners"><grandson>

孙子

this.$emit('action',msg);

有的时候,我们还可以用computed覆写$listeners这个事件监听器

computed:{
	mylisteners:function(){
		var _this = this;
		return Object.assign({},this.$listens,{
			input:function()(event) {
            _this.$emit('input', event.target.value)
          }
		}
	}
}

.sync

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。

这也是为什么我们推荐以 update:myPropName 的模式触发事件取而代之。举个例子,在一个包含 title prop 的假设的组件中,我们可以用以下方法表达对其赋新值的意图:

this.$emit('update:title', newTitle)

然后父组件可以监听那个事件并根据需要更新一个本地的数据属性。例如:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

为了方便起见,我们为这种模式提供一个缩写,即 .sync 修饰符:

<text-document v-bind:title.sync="doc.title"></text-document>

所以.sync的作用就是

<text-document v-on:update:tiltle="doc.title=$event"></text-document>

注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的属性名,类似 v-model。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章