首页 > 编程笔记

Vue子组件给父组件传值详解

组件的 prop 属性只能实现父组件向子组件传值,在实际的前端项目中,需要实现子组件将值传给父组件。Vue.js 提供了 3 种机制,实现子组件将值传给父组件。

1、使用$emit方法调用父组件方法传值

在 Vue.js 的父组件中,可以通过 v-on 指令,给子组件的指定事件绑定一个函数,在子组件中,用 $emit 方法触发自己的事件,从而执行被绑定的函数。

$emit 方法的第 1 个参数是一个字符串,对应 v-on 指定的事件名称,父组件中使用 v-on 给 son-component 组件的 parent-method 事件绑定了定义在父组件中 parentMethod 函数,在 son-component 的 toTest 函数中,使用 this.$emit('parent-method') 方式触发 parent-method 事件,执行 parentMethod 方法,实现父组件中的 count 自增,代码如下:
<div id="app">
  <son-component v-on:parent-method="parentMethod"></son-component>
  <br />
  <div>{{ count }}</div>
</div>

<template id="sonComponent">
  <button v-on:click="toTest">单击子组件</button>
</template>

<script type="text/javascript">
const SonComponent = {
  template: '#sonComponent',
  methods: {
    toTest() {
      this.$emit('parent-method');
    }
  }
};

const vm = new Vue({
  el: '#app',
  components: {
    SonComponent
  },
  data: {
    count: 0
  },
  methods: {
    parentMethod() {
      this.count++;
    }
  }
});
</script>
$emit 方法必须有一个参数指定要触发的事件,同时支持更多的可选参数,通过这些参数,子组件可以将自己的数据传递给事件绑定的方法,而绑定的方法是定义在父组件中的,所以就可以间接地使用 $emit 方法,将子组件中的数据传递给父组件。

在 son-component 子组件的 toTest 方法中,通过第 2 个参数给父组件中绑定的 parentMethod 方法传递 count 的递增幅度 step,代码如下:
<div id="app">
  <son-component v-on:parent-method="parentMethod"></son-component>
  <br />
  <div>{{ count }}</div>
</div>

<template id="sonComponent">
  <button v-on:click="toTest">单击子组件</button>
</template>

<script type="text/javascript">
const SonComponent = {
  template: '#sonComponent',
  methods: {
    toTest() {
      this.$emit('parent-method', 2);
    }
  }
};

const vm = new Vue({
  el: '#app',
  components: {
    SonComponent
  },
  data: {
    count: 0
  },
  methods: {
    parentMethod(step) {
      this.count += step;
    }
  }
});
</script>

2、调用父组件的方法传值

prop 属性的数据类型支持 Function,利用这个特点,开发人员可以在父组件中定义一个 Function 类型的 prop 属性,给子组件传递一个函数对象,在子组件中调用这个函数,通过函数的参数,可以将子组件中的数据传递给父组件。

父组件基于子组件的 funcData prop 属性,给子组件 son-component 传递 increment 函数对象,在子组件 son-component的toTest 方法中,调用传入的 increment 函数,并且传入参数 step 的值,代码如下:
<div id="app">
  <son-component v-bind:func-data="increment"></son-component>
  <br />
  {{ count }}
</div>

<template id="sonComponentTemplate">
  <button v-on:click="toTest">单击递增</button>
</template>

<script type="text/javascript">
const SonComponent = {
  template: '#sonComponentTemplate',
  props: {
    funcData: {
      type: Function
    }
  },
  methods: {
    toTest() {
      this.funcData(2);
    }
  }
};

const vm = new Vue({
  el: '#app',
  components: {
    SonComponent
  },
  data: {
    count: 0
  },
  methods: {
    increment(step) {
      this.count += step;
    }
  }
});
</script>

3、使用v-model实现父子组件的数据同步

v-model 指令可以实现 input 输入框同组件数据属性双向同步,改变输入框的值,此值能自动被同步到 Vue.js 实例对象中。同样,改变 Vue.js 实例对象的数据属性,此数据属性也能自动被同步到 input 输入框,代码如下:
<div id="app">
  name: {{ name }}<br />
  <input v-model="name" /><br />
</div>

<script type="text/javascript">
const vm = new Vue({
  el: '#app',
  data: {
    name: ''
  }
});
</script>
实际上,v-model 是 v-bind:value 和 v-on:input 两个指令的组合:
input 元素中的 b-bind:value='name',将 name 数据属性的值绑定到 input 的 value 属性上,这样 input 输入框就可以实时显示 name 数据属性的值了。

input 元素中的 v-on:input="demoInputChange($event)" 将 demoInputChange 函数绑定到 input 元素的 input 事件上,并且传入了当前的事件对象,当 input 事件触发时自动执行 demoInputChange 函数,将 input 的 value 属性的值赋给 name 数据属性,从而实现了 input 元素中的 value 同 Vue.js 实例对象中的 name 数据属性的双向绑定,代码如下:
<div id="app">
  name: {{ name }}<br />
  <input v-bind:value="name" v-on:input="demoInputChange($event)" /><br />
</div>

<script type="text/javascript">
const vm = new Vue({
  el: '#app',
  data: {
    name: ''
  },
  methods: {
    demoInputChange(event) {
      this.name = event.target.value;
    }
  }
});
</script>
既然 v-bind 和 v-on 的组合可以实现 input 元素的 value 属性同 Vue.js 实例对象的数据属性的双向绑定,同样可以用在子组件上,实现子组件的 value 和数据属性的双向绑定,代码如下:
<div id="app">
  <!-- 子组件使用v-bind和v-on:input的组合 -->
  age: {{ age }}<br />
  <son-component v-bind:age="age" v-on:input="sonChange"></son-component>
</div>

<template id="sonComponentTemplate">
  <input type="text" v-bind:value="age" v-on:input="toChange($event)" />
</template>

<script type="text/javascript">
const SonComponent = {
  template: '#sonComponentTemplate',
  props: ['age'],
  methods: {
    toChange(event) {
      this.$emit('input', event.target.value);
    }
  }
};

const vm = new Vue({
  el: '#app',
  components: {
    SonComponent
  },
  data: {
    age: 0
  },
  methods: {
    sonChange(age) {
      this.age = age;
    }
  }
});
</script>
使用 v-model 合并子组件的 v-bind 和 v-on 指令,代码如下:
<div id="app">
  <!-- 子组件使用v-bind和v-on:input的组合 -->
  age: {{ age }}<br />
  <son-component v-model="age"></son-component>
</div>

<template id="sonComponentTemplate">
  <input type="text" v-bind:value="age" v-on:input="toChange($event)" />
</template>

<script type="text/javascript">
const SonComponent = {
  template: '#sonComponentTemplate',
  props: ["age"],
  methods: {
    toChange(event) {
      this.$emit('input', event.target.value);
    }
  }
};

const vm = new Vue({
  el: '#app',
  components: {
    SonComponent
  },
  data: {
    age: 0
  }
});
</script>

推荐阅读