Vue之组件传值

在学习前端框架Vue时,遇到了组件之间的传值问题,索性take a note。

组件

父子组件

父子关系即是组件 A 在它的模板中使用了组件 B,那么组件 A 就是父组件,组件 B 就是子组件。

我们可以这样理解:Vue实例就是一个父组件,而我们定义的组件(包括全局组件、私有组件)就都是子组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注册一个子组件
Vue.component('child', {
data: function(){
return {
text: '我是father的子组件!'
}
},
template: '<span>{{ text }}</span>'
})
// 注册一个父组件
Vue.component('father', {
template: '<div><child></child></div>' //在模板中使用了child组件
})

直接使用father组件的时候:

1
2
3
<div id="app">
<father></father>
</div>

页面中就会渲染出 :我是father的子组件!

father 组件在模板中使用了 child 组件,所以它就是父组件,child 组件被使用,所以 child 组件就是子组件。

兄弟组件

两个组件互不引用,则为兄弟组件。

1
2
3
4
5
6
Vue.component('brother1', {
template: '<div>我是大哥</div>'
})
Vue.component('brother2', {
template: '<div>我是小弟</div>'
})

使用组件的时候:

1
2
3
4
<div id="app">
<brother1></brother1>
<brother2></brother2>
</div>

页面中就会渲染出 :

我是大哥

我是小弟

Prop

子组件想要使用父组件的数据,我们需要通过子组件的 props 选项来获得父组件传过来的数据。以下我使用在 .vue 文件中的格式来写例子。

在父组件 father.vue 中引用子组件 child.vue,把 name 的值传给 child 组件。

1
2
3
4
5
6
7
8
9
10
// child.vue
<template>
<span>Hello {{message}}</span>
</template>
<script>
export default {
// 在 props 中声明获取父组件的数据通过 message 传过来,message为值的标签
props: ['message']
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//father.vue
<template>
<div class="app">
// message 定义在子组件的 props 中,message为值的标签,name为值的key
<child :message="name"></child>
</div>
</template>
<script>
import child from './child.vue';
export default {
components: {
child
},
data() {
return {
name: 'World'
}
}
}
</script>

页面会渲染出Hello World

举例2:

在父组件传值的子组件内部定义prop数组,将父组件内部的data内部要传的数据的名称作为子组件引用值的属性的值,

如,有子组件component1,父组件div,在父组件内部引用子组件,子组件使用父组件的值,

要传父组件data中的msg:’父组件中的数据’,在子组件使用父组件的值时,如下使用v-bind定义属性名parent-msg作为使用值的标签(你也可以设置为:parentMsg)所以,在目的地子组件内部定义prop属性,值设置为parentMsg(驼峰式命名的 prop 需要转换为相对应的短横线隔开式 is-show)。然后:parentMsg的值,为msg(要传父组件data中的值的keyName)

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="vue2.5.16.js"></script>
</head>

<body>
<div id="app">
<!-- 第三步:父组件在引用子组件的时候, 通过 属性绑定(v-bind:)的形式, -->
<!--把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
<component1 v-bind:parent-msg="msg"></component1>
</div>

<!-- 定义子组件的模板 -->
<template id="myTemplate">
<!-- 第二步:在子组件的模板中,使用props中的属性 -->
<h2 @click="change">我是子组件。我想使用父组件中的数据parentMsg: {{ parentMsg }}</h2>
</template>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app', //在外面定义了id为app的组件作为父组件
data: {
msg: '父组件中的数据'
},
methods: {},
//自定义组件
components: {
// 自定义组件作为子组件时默认无法访问到父组件中的 data 中的数据 和 methods 中的方法
component1: { //将组件的名称定义为 component1
template: '#myTemplate', //在外面定义了id为myTemplate的template
data() { // 注意: 自定义组件中的 data 数据,并不是通过 父组件传递过来的,而是组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
// data 上的数据,都是可读可写的
return {
title: '自定义组件私有的数据 title',
content: '自定义组件私有的数据 content'
}
},

// 注意: 组件中的 所有 props 中的数据,都是通过 父组件 传递给子组件的
// props 中的数据,都是只读的,无法重新赋值
props: ['parentMsg'], // 第一步:把父组件传递过来的 parentMsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
directives: {},
filters: {},
components: {},
methods: {
change() {
// 下面这行会报错,因为子组件不要直接修改父组件中的data数据
// this.parentMsg = '被修改了'
}
}
}
}
});
</script>
</body>

</html>

子组件中,data中的数据和props中的数据的区别

  • 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上。props 中的数据,都是通过 父组件 传递给子组件的。
  • data中的数据是可读可写的;props中的属性只是可读的,无法重新赋值,重新赋值会报错(也就是说,子组件不要直接去修改父组件中的数据)。

父组件将方法传递给子组件

父组件通过事件绑定机制,将父组件的方法传递给子组件

代码举例:

下面例子,我们来演示 当点击子组件的按钮时,拿到父组件传递过来的 方法,并调用这个方法

我们在子组件的method中定义了一个函数来实现调用父组件的show函数,通过调用$emit函数,在子组件地方使用了@关键字来表示要调用父组件的事件属性parent-show,然后将函数名作为值,此时,对于$emit函数,我们使用事件属性名parent-show作为参数传入。

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="vue2.5.16.js"></script>
</head>

<body>
<div id="app">
<!-- 父组件向子组件 传递 方法,是通过 事件绑定机制; v-on。当我们自定义了 一个 事件属性 parent-show(这 个地方不能用驼峰命名)之后,-->
<!-- 那么,子组件就能够,通过 emit 来调用 传递进去的 这个 方法了 -->
<!-- 【第一步】。意思是说,show是父组件的方法名,parent-show是自定义的事件属性,稍后要在子组件中用 到 -->
<component1 @parent-show='show'></component1>
</div>

<!-- 定义子组件的模板 -->
<template id="myTemplate">
<!-- 【第二步】按照正常的写法来:点击按钮,调用子组件的方法 -->
<div @click="childClick">我是子组件,点击调用父组件的方法</div>
</template>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: { //父组件的data

},
methods: {
show: function () { // 定义父组件的show方法
console.log('父组件提供的方法');
}
},
components: {
component1: { //将子组件的名称定义为 component1
template: '#myTemplate',
data() { // 子组件的data
return {
// content: '子组件私有的数据 content'
}
},
props: [''],
directives: {},
filters: {},
components: {},
methods: {
childClick() {

// emit 英文原意: 是触发,调用、发射。意思是,触发父组件的方法
// 【第三步】 在子组件的方法中,通过 emit 触发父组件的方法
this.$emit('parent-show');
}
}
}
}
});
</script>
</body>

</html>

效果:(点击子组件,触发了父组件的方法)

在这个过程中,我们对父组件传递的show的参数做一点点修改增加了两个参数,并实现将两个参数的值打印出来。

所以,在调用$emit函数的时候,就必须多传入两个参数。这样,我们实现了在子组件内部将数据作为参数传入到父组件。

代码如下:

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="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="vue2.5.16.js"></script>
</head>

<body>
<div id="app">
<component1 @parent-show='show'></component1>
</div>

<!-- 定义子组件的模板 -->
<template id="myTemplate">
<h2 @click="childClick">我是子组件,点击调用父组件的方法</h2>
</template>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: { //父组件的data
// msg: '父组件中的数据'
},
methods: { // 定义父组件的方法
show: function (arg1, arg2) { //【第二步】父组件里放两个参数,这个两个参数就代表着子组件中的`child 123`、`child 789`
console.log('父组件提供的方法');
console.log('打印子组件传递过来的参数。参数一:' + arg1 + ',参数二:'+ arg2);
}
},
components: {
component1: { //将子组件的名称定义为 component1
template: '#myTemplate',
data() { // 子组件的data
return {
// content: '子组件私有的数据 content'
}
},
props: [''],
directives: {},
filters: {},
components: {},
methods: {
childClick() {
// 子组件如果要给父组件传递参数,在触发 emit 的时候,通过参数的形式带出去就可以了
// 【第一步】在子组件里,我们带两个参数出去,传给父组件
this.$emit('parent-show', 'child 123', 'child 789');
}
}
}
}
});
</script>
</body>

</html>

我们还可以将子组件中的data数据传递给父组件,存放到父组件的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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="vue2.5.16.js"></script>
</head>

<body>
<div id="app">
<component1 @parent-show='show'></component1>
</div>

<!-- 定义子组件的模板 -->
<template id="myTemplate">
<h2 @click="childClick">我是子组件,点击调用父组件的方法</h2>
</template>

<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: { //父组件的data
parentData: null
},
methods: { // 定义父组件的方法
show: function (arg) { //【第二步】父组件里放参数,这个参数就代表着子组件中的 child.data
console.log('父组件提供的方法');
this.parentData = arg; //将参数arg传递给父组件的data,也就达到了目的:子组件传递数据,赋值给父组件
console.log('打印父组件的数据(这是子组件传过来的):'+ JSON.stringify(this.parentData));
}
},
components: {
component1: { //将子组件的名称定义为 component1
template: '#myTemplate',
data() { // 子组件的data
return {
childData: { //定义自组件的数据
name: 'smyhvae',
age: 26
}
}
},
props: [''],
directives: {},
filters: {},
components: {},
methods: {
childClick() {
// 子组件如果要给父组件传递参数,在触发 emit 的时候,通过参数的形式带出去就可以了
// 【第一步】在子组件里,通过传参的形式,把子组件的data,传给父组件
this.$emit('parent-show', this.childData);
}
}
}
}
});
</script>
</body>

</html>

在Vue中,通过 ref 属性获取DOM元素

我们当然可以使用JS原生的做法(document.getElementById)或者 jQuery 来获取DOM,但是这种做法却在无形中操作了DOM,在Vue框架中并不推荐这种做法。

我们可以通过ref属性获取DOM元素。

ref的英文单词是reference,表示引用。我们平时可以经常看到控制台会报错referenceError的错误,就和引用类型的数据有关。

在Vue中,通过 ref 属性获取DOM元素的步骤:

(1)第一步:在标签中给 DOM 元素设置 ref 属性。

1
<h3 id="myH3" ref="myTitle"> 今天天气太好了</h3>

(2)第二步:通过 this.this.$refs.xxx 获取 DOM 元素

1
console.log(this.$refs.myTitle.innerText)

举例如下

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="vue2.5.16.js"></script>
</head>

<body>
<div id="app">

<!-- 第一步:在标签中给 DOM 元素设置 ref 属性 -->
<h3 id="myH3" ref="myTitle"> 今天天气太好了</h3>

<input type="button" value="按钮元素" @click="getElement" ref="myBtn">


</div>

<script>

var login = {
template: '<h1>登录组件</h1>',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法')
}
}
}

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
// 原生js获取DOM元素
// console.log(document.getElementById('myTitle').innerText)

// 第二步:通过 this.this.$refs.xxx 获取 DOM 元素
console.log(this.$refs.myTitle.innerText)


}
},
components: {
login
}
});
</script>
</body>

</html>

运行上方代码,然后我们在控制台输入vm,就可以看到:

img

使用 ref 属性获取整个子组件

根据上面的例子,我们可以得出规律:只要ref属性加在了DOM元素身上,我们就可以获取这个DOM元素。

那我们可以通过ref属性获取整个Vue子组件吗?当然可以。这样做的意义是:**在父组件中通过ref属性拿到了子组件之后,就可以进一步拿到子组件中的data和method。

举例:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="vue2.5.16.js"></script>
</head>

<body>
<div id="app">

<input type="button" value="点击按钮" @click="getElement">

<login-component ref="loginTemplate"></login-component>
</div>

<script>

// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {

//在父组件中,通过ref获取整个子组件,进而获取子组件的data
console.log(this.$refs.loginTemplate.myData)

//在父组件中,通过ref获取整个子组件,进而获取子组件的method
this.$refs.loginTemplate.showMethod()
}
},
components: {
'login-component': {
template: '<h1>登录组件</h1>',
data() {
return {
myData: '子组件的data'
}
},
methods: {
showMethod() {
console.log('调用子组件的method')
}
}
}
}
});
</script>
</body>

</html>

运行代码,点击按钮后,效果如下:

img

我们直接在控制台输入vm,可以看到:

img

不要投钱给我
0%