banner
NEWS LETTER

彻底搞懂组件通信:从手动绑定到 v-model 语法糖

Scroll down

在 Vue 2 开发中,父子组件的数据同步是最高频的场景。很多新手容易在“单向数据流”和“双向绑定”之间绕晕。

今天我们通过两个具体的案例——“手动双向绑定”“v-model 语法糖”,来彻底总结组件通信的核心要点。

案例一:手动实现双向绑定 (Props + Emit)

这是 Vue 最基础的通信方式,遵循“Props 向下,Events 向上”的原则。为了不违背单向数据流(子组件不能直接改 Props),我们需要在子组件里把 Props 转存为 Data。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>手动双向绑定</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app">
<h3>案例一:手动绑定</h3>
<!-- 1. 父传子:通过属性 :child-num -->
<!-- 2. 子传父:通过事件 @update-num -->
<cpn :child-num="parentNum" @update-num="handleUpdate"></cpn>

<p>父组件的数据: {{ parentNum }}</p>
</div>

<template id="cpn">
<div style="background: #eee; padding: 10px;">
<h4>Props值: {{ childNum }}</h4>
<h4>Data值: {{ localNum }}</h4>
<!-- 3. 绑定 input 事件,修改 data 并发射事件 -->
<input type="text" :value="localNum" @input="onInput">
</div>
</template>

<script>
const cpn = {
template: "#cpn",
props: {
// 接收父组件数据
childNum: Number
},
data() {
return {
// 【重点】不要直接改 props,要在 data 里克隆一份
localNum: this.childNum
};
},
methods: {
onInput(e) {
// 1. 获取输入框的值(注意是字符串)
let val = e.target.value;

// 2. 只有转成数字,逻辑才严谨
val = Number(val);

// 3. 修改子组件自己的 data
this.localNum = val;

// 4. 发射事件通知父组件
// 注意:这里的 this 指向子组件实例
this.$emit("update-num", val);
}
}
};

const app = new Vue({
el: "#app",
data() {
return {
parentNum: 100
};
},
components: { cpn },
methods: {
handleUpdate(val) {
// 父组件接收新值并更新
this.parentNum = val;
}
}
});
</script>

</body>
</html>

核心逻辑解析

  1. Props 初始化 Data:在 data() 中使用 this.childNum 将父组件传来的值作为初始值。
  2. 修改本地 Data:用户输入时,先修改 this.localNum
  3. 通知父组件:使用 this.$emit 告诉父组件“我也变了,你那边也改一下吧”。

案例二:使用 v-model (语法糖)

Vue 为了简化上面的操作,提供了 v-model。在 Vue 2 中,v-model 本质上就是 :value + @input 的缩写。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>v-model 语法糖</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app">
<h3>案例二:v-model 语法糖</h3>
<!--
【重点】这里直接写 v-model="parentNum"
它等价于::value="parentNum" @input="parentNum = $event"
-->
<cpn v-model="parentNum"></cpn>

<p>父组件的数据: {{ parentNum }}</p>
</div>

<template id="cpn">
<div style="background: #eef; padding: 10px;">
<!-- 子组件直接用 value 接收 -->
<h4>Props(value): {{ value }}</h4>
<input type="text" :value="value" @input="onInput">
</div>
</template>

<script>
const cpn = {
template: "#cpn",
// 【重点规则】v-model 默认会找名为 value 的 prop
props: {
value: Number
},
methods: {
onInput(e) {
// 1. 转换类型
const val = Number(e.target.value);

// 2. 【重点规则】v-model 默认监听名为 input 的事件
// 这里直接发射,甚至不需要在子组件 data 里存一份
this.$emit("input", val);
}
}
};

const app = new Vue({
el: "#app",
data() {
return {
parentNum: 666
};
},
components: { cpn }
});
</script>

</body>
</html>

核心逻辑解析

  1. 固定 Props 名:子组件必须接收一个叫 value 的 prop(这是 Vue 2 v-model 的默认规矩)。
  2. 固定事件名:子组件必须发射一个叫 input 的事件。
  3. 父组件超简洁:直接写 v-model,不用写 method 去接收,Vue 帮你自动处理赋值。

总结:Vue 2 组件通信的 4 个“死规定”

通过对比这两个案例,我们在写代码时必须牢记以下几点(考试/面试必问):

1. 单向数据流 (One-Way Data Flow)

  • 死规定绝对不能在子组件里直接修改 Props!
  • 如果你在子组件写 this.dnum1 = 100,Vue 会在控制台报错。
  • 做法:要么像案例一那样把 Props 转给 Data,要么像案例二那样直接 Emit 给父组件改。

2. 所有的 Data 必须是函数

  • 死规定data() { return { ... } }
  • 如果写成对象 data: { ... },组件复用时数据会乱套。

3. Input 取值永远是字符串

  • 坑点e.target.value 拿到的永远是 String。
  • 做法:如果你需要数字,必须手动用 Number()parseFloat() 转换,否则 1 + 1 会变成 '11'

4. 这里的 this 不能省

  • <script> 标签里(methods, created 等),访问数据必须加 this.
  • 比如 this.$emitthis.value。漏了 this 就会报错找不到变量。

建议
建议先熟练掌握案例一(手动绑定),因为它逻辑最清晰,能帮你理解数据是怎么流动的。等熟悉了之后,再使用 v-model 这种简写方式来提高开发效率!

其他文章
目录导航 置顶
  1. 1. 案例一:手动实现双向绑定 (Props + Emit)
    1. 1.1. 代码实现
    2. 1.2. 核心逻辑解析
  2. 2. 案例二:使用 v-model (语法糖)
    1. 2.1. 代码实现
    2. 2.2. 核心逻辑解析
  3. 3. 总结:Vue 2 组件通信的 4 个“死规定”
    1. 3.1. 1. 单向数据流 (One-Way Data Flow)
    2. 3.2. 2. 所有的 Data 必须是函数
    3. 3.3. 3. Input 取值永远是字符串
    4. 3.4. 4. 这里的 this 不能省