相较于props,用自定义事件的方法实现子组件给父组件传参更合理和方便,但props也确实可以实现 ‘子传父’。

例子中一共有三个组件 App组件,Helloworld组件,ChildrenVue组件。app组件管理后两个组件

APP组件中的内容

//App.vue
... 
    <HelloWorld :msg="msg"></HelloWorld>
    <ChildrenVue></ChildrenVue>
...
import HelloWorld from './components/HelloWorld.vue'
import ChildrenVue from './components/ChildrenVue.vue';
export default {
  name: 'App',
  data(){
    return {
        msg:'Hollo,I`m a Vue.js app.'
    }
  },
  components: {
    HelloWorld,
    ChildrenVue
  }
}
...

HelloWorld组件中的内容

//HelloWorld.vue
...
 <div class="hello">
    <h1>{{msg}}</h1>
 </div>
...
export default {
  name: 'HelloWorld',
  props: {
    msg: {type:String, default:'Send message to me,OK?'},
  }}
...

ChildrenVue组件中的内容

...
    <div id="children">
    </div>
...
export default {
    name:'ChildrenVue',
}

用Vue脚手架工具生成的页面,手动添加了一个子组件,并更改了默认msg传参的内容。

props实现子传父

props传参同props父传子参数类似,具体实现原理是父组件定义函数,并将函数传递给子组件,在子组件中调用,并将自己的数据作为参数传递给父组件的函数。大体可分为四步。

第一步,在子组件 标签上 上使用v-bind(简写为 :)绑定传递的参数名以及传递的处理函数;

第二步,在子组件的 script 中 添加 props 属性 ,并将 参数名 接收;

第三步,在子组件中调用其他函数来触发用 props接收的参数(也就是那个函数)

第四步,子组件的参数被传递出去

用到APP和Children两个组件

APP组件中的内容

//App.vue
... 
    <HelloWorld :msg="msg"></HelloWorld>
    <ChildrenVue :getmsg="getmsg"></ChildrenVue>
...
import HelloWorld from './components/HelloWorld.vue'
import ChildrenVue from './components/ChildrenVue.vue';
export default {
  name: 'App',
  data(){
    return {
        msg:'Hollo,I`m a Vue.js app.'
    },
  methods:{
    getmsg(msg){
      console.log(msg);
    }
   },
  components: {
    HelloWorld,
    ChildrenVue
  }
}
...

ChildrenVue组件中的内容

...
    <div id="children">
    	<button @click="sendmsg">sendmsg</button>
    </div>
...
export default {
    name:'ChildrenVue',
    props:[ 'getmsg' ],
    data(){
        return {
            msg:'I`m children'
        }
    },
    methods: {
        sendmsg(){
            return this.getmsg(this.msg)
        }
    },
}

实现效果是控制台输出 I`m children 。

自定义事件实现子向父传递参数

自定义事件有两种方法触发,先说第一种。
其主要实现也是靠函数参数传递的方法将数据带给父组件,具体分三步:
第一步,在子组件 标签上 上使用v-on(简写为 @)绑定自定义的 事件名 以及对应的处理函数,可以类比原生事件 @click=“handle(e)”;

第二步,在子组件中调用其他函数,使用 this.$emit(‘事件名’,参数) 的方法触发事件;

第三步,子组件的参数被传递出去

这次用app和helloworld两个组件

APP组件中的内容

//App.vue
... 
    <HelloWorld :msg="msg" @addnumber='addnumber'></HelloWorld>
    <ChildrenVue :getmsg="getmsg"></ChildrenVue>
...
import HelloWorld from './components/HelloWorld.vue'
import ChildrenVue from './components/ChildrenVue.vue';
export default {
  name: 'App',
  data(){
    return {
        msg:'Hollo,I`m a Vue.js app.'
    },
  methods:{
    getmsg(msg){
      console.log(msg);
    },
    addnumber(num){
    	console.log(num+10);
    }
   },
  components: {
    HelloWorld,
    ChildrenVue
  }
}
...

HelloWorld组件中的内容

//HelloWorld.vue
...
 <div class="hello">
    <h1>{{msg}}</h1>
    <button @click="sendnumber">addnumber</button>
 </div>
...
export default {
  name: 'HelloWorld',
  data(){
    return {
      num:30
    }
  },
  props: {
    msg: {type:String, default:'Send message to me,OK?'},
  },
  methods:{
    sendnumber(){
      this.$emit('addnumber',this.num)
    }
  }    
}

...

最终效果是控制台输出 40

第二种自定义事件的方式

到目前为止,props和自定义事件好像差别并不大,自定义事件虽然少写了写参数,且子组件看着更简洁,并没有什么明显的过人之处,现在看第二种方法:

APP组件中的内容

//App.vue
... 
    <HelloWorld :msg="msg" ref="Helloworld"></HelloWorld>
    <ChildrenVue :getmsg="getmsg"></ChildrenVue>
...
import HelloWorld from './components/HelloWorld.vue'
import ChildrenVue from './components/ChildrenVue.vue';
export default {
  name: 'App',
  data(){
    return {
        msg:'Hollo,I`m a Vue.js app.'
    },
  methods:{
    getmsg(msg){
      console.log(msg);
    },
    addnumber(num){
    	console.log(num+10);
    }
   },
  components: {
    HelloWorld,
    ChildrenVue
  },
  mounted(){
   this.$refs.Helloworld.$on('addnumber', this.addnumber)
  }
}
...

HelloWorld组件不需要任何变化
主要变动有两处,第一处 是子组件标签中 使用ref属性;第二处 是不在methods或者watch中 定义处理函数,而是在生命周期函数中处理。
这样的好处是更加灵活,自定义事件可以添加其他异步操作,且对子组件绑定多个自定义事件时,也无需考虑太多的逻辑,不断地在 生命周期函数中 使用 $on 来处理即可。

由上可以看出 $on 接收两个参数,$on(自定义事件名,函数) 而函数可以用箭头函数来简写操作,优化后的代码如下:
APP组件中的内容

//App.vue
... 
    <HelloWorld :msg="msg" ref="Helloworld"></HelloWorld>
    <ChildrenVue :getmsg="getmsg"></ChildrenVue>
...
import HelloWorld from './components/HelloWorld.vue'
import ChildrenVue from './components/ChildrenVue.vue';
export default {
  name: 'App',
  data(){
    return {
        msg:'Hollo,I`m a Vue.js app.'
    },
  methods:{
    getmsg(msg){
      console.log(msg);
    }
   },
  components: {
    HelloWorld,
    ChildrenVue
  },
  mounted(){
   this.$refs.Helloworld.$on('addnumber', num =>{ console.log(num+10) })
  }
}
...

但需要注意, $on 中定义的函数this是指向 调用该事件的组件,也就是子组件,若我想在html页面中打印内容,则需这样写:
APP组件中的内容

//App.vue
... 
    <HelloWorld :msg="msg" ref="Helloworld"></HelloWorld>
    <p>子组件的数计算后是:{{addnumber}}</p>
    <ChildrenVue :getmsg="getmsg"></ChildrenVue>
...
import HelloWorld from './components/HelloWorld.vue'
import ChildrenVue from './components/ChildrenVue.vue';
export default {
  name: 'App',
  data(){
    return {
        msg:'Hollo,I`m a Vue.js app.',
        addnumber: undefined
    },
  methods:{
    getmsg(msg){
      console.log(msg);
    }
   },
  components: {
    HelloWorld,
    ChildrenVue
  },
  mounted(){
   this.$refs.Helloworld.$on('addnumber', num =>{ 
   		console.log(num+10);
   		this.addnumber = num+10; })
  }
  /*错误写法
   mounted(){
   	this.$refs.Helloworld.$on('addnumber', function(num){ 
   		console.log(num+10)
   		this.addnumber = num+10 })
  	 }
   }*/
}
...

使用箭头函数可以避免this指向 “错误” ,函数内部更改的值为data里的数据。

自定义事件的扩展

自定义事件也有 $once 可以触发一次自定义事件。
使用自定义事件要注意区分开原生事件,可以归纳为两句话:
原生DOM节点用自定义事件没有意义,
自定义节点只能用自定义事件
例如 在自定义节点用

...
<HelloWorld :msg="msg" @click='addnumber'></HelloWorld>
...

意思是一个事件名为click的自定义事件,若想用原生事件,需要这样写

...
<HelloWorld :msg="msg" @click.native='addnumber'></HelloWorld>
...

总结

在子组件向父组件传参时,优先考虑的还是自定义事件的方法(当然事件总线,vuex更优先,总之不要props)。
在没有复杂逻辑时,可以直接用 @自定义事件名=‘处理函数’ 的方法处理,而在子组件,可以用 @click=‘$emit(事件名, 要传的参数)’ 来快速相应(正常开发没人这么用吧)。

Logo

前往低代码交流专区

更多推荐