• Vue前端面试必考题:组件间的传值方式有哪些?
  • 发布于 2个月前
  • 111 热度
    0 评论
必考题,但要全部说清楚,不容易。且看且珍惜。

props和$emit
props 是子组件接收来自父组件的属性,父组件调用子组件的时候,通过传入这个属性,子组件就可以直接像使用data里面的参数一样,使用这个属性了。来看一个例子,子组件:
<template>
  <div>
    <h1 style="font-size: 30px">My name is {{name}}</h1>
  </div>

</template>

<script>
export default {
  name: "son",
  props:{
    name:String
  }
}
</script>
然后是父组件
<template>
  <div>
    <son :name="mySon"></son>
  </div>

</template>

<script>
import son from './son'
export default {
  name: "father",
  components: {son},
  data(){
    return {
      mySon: 'jack'
    }
  }
}
</script>
渲染的时候,子组件就可以拿到传过去的jack,实现了父组件向子组件的传递。
$emit子组件怎么向父组件传值呢?比如子组件有一个按钮
<button @click="action">点我吧</button>
点击事件:
action() {
  this.$emit('action',name);
}
action是子组件触发的事件名称,我们还需要在父组件中进行绑定。来到父组件:
<son :name="mySon" @action="actionInFacther"></son>
actionInFacther才是父组件实际的方法名称。
actionInFacther(name) {
  alert(name)
}
当我们点击子组件的按钮,先通过this.$emit('action',name);得知,要去触发action事件,而action事件在父组件中,又跟actionInFacther绑定了,所以实际触发的就是actionInFacther方法。

自定义事件(bus)
在Vue2中,我们直接通过 new Vue()的方式,就可以实现一个总线,但是在Vue3中是不支持的,要去引入第三方库,比如event-emitter。我们以Vue2为例,一般会在main.js中绑定全局Bus。
new Vue({
  store, router, beforeCreate() {
    Vue.prototype.$bus = this // 安装全局事件总线
  }, render: (h) => h(App)
}).$mount('#app');
然后在父组件的mounted方法中,绑定自定义事件。
mounted() {
  this.$bus.$on("sizeChange", size => {
    this.table.pageConfig.size = size
    this.loading()
  })
},
然后在子组件中,触发这个事件:
this.$bus.$emit('sizeChange', size)
最后,在父组件的beforeDestroy方法中,要去销毁这个事件。
beforeDestroy() {
  // 销毁事件
  this.$bus.$off("sizeChange")
}
这种方式呢,不限于父子组件之间通讯,比较灵活,也是项目中使用比较多的。

$attrs 跨代传输
$attrs算是父子传参的一个补充,比如,我们在father给son多传递一个参数。
<son :name="mySon" :grandFather="'Tom'" @action="actionInFacther"></son>
但是呢,grandFather这个属性,son组件不接收。并且,son组件又引入了一个子组件grandson。
grandson
<template>
  <div>
    <h1 style="font-size: 30px">My grandFather is {{grandFather}}</h1>
  </div>

</template>

<script>
export default {
  name: "son",
  props:{
    grandFather:String
  },
  methods:{
   // 堆代码 duidaima.com
  },

}
</script>
孙子组件需要grandFather属性,而这个属性在father属性中有,son组件没有接收,这种父组件有,但是子组件没有显式接收的属性,都会被放在$attrs中。于是就有一个v-bind的写法:
<template>
  <div>
    <h1 style="font-size: 30px">My name is {{name}}</h1>
    <button @click="action">点我吧</button>

    <grandson v-bind="$attrs"></grandson>
  </div>

</template>

<script>
import grandson from './grandson'
export default {
  name: "son",
  components:{grandson},
  props:{
    name:String
  },
  methods:{
    action() {
      this.$emit('action',this.name);
    }
  },

}
这样就实现了隔代传输,孙子组件就可以拿到这些属性了。

refs
对于父子组件的调用,我们可以直接使用$parent和$refs来完成,子组件通过$parent就可以直接拿到父组件的对象,然后调用其属性和方法。比如,在子组件中,我们可以这样来触发点击事件:
action() {
  // this.$emit('action',this.name);
  this.$parent.actionInFacther(this.name);
}
在father组件中,可以通过$refs来拿到子组件的属性和方法。
mounted() {
  console.log(this.$refs.son.name)
}
注意,这种方式不能写在created中,因为created的时候,可能dom还未完全挂载,建议还是写在mounted里面。

provided和inject
provided和inject属于一种父组件提供,任何子组件随时获取的模式,非常灵活。而且,它也支持响应式的数据传输。
我们修改一下父组件,加一个input框。
<input type="text" v-model="keywords">
引入computed
import {computed} from 'vue'
以函数的形式设置provided:
provide(){
  return {
    keywords: computed(() => this.keywords)
  }
}
最后,在孙子组件中用inject去接收
inject:['keywords']
然后,在孙子组件中就可以直接使用keywords了,还是响应式的。

总结
1.适用于父子组件的:props和@emit,和refs
2.适用于上下级(跨多级)的:$attrs,provide和inject
3.适用于全局组件的: 自定义事件(bus)
用户评论