Vue组件基础与父子组件之间的值传递方式
一、组件基本示例
1.1 组件的概念
组件从形式上看就是一个自定义的HTML标签,组件是可复用的vue实例,要构造函数Vue的子类。
1.1.1 全局组件
- 创建一个组件分为3步,这里有一个Vue组件的实例:
<script>
// 第一步:创建组件
const childComponent = Vue.extend({
template: `<h2>Hello World</h2>`
});
// 第二步:注册组件
Vue.component("child-component", childComponent);
// Vue实例
const vm = new Vue({
el: "#app"
});
</script>
<!-- 第三步:将组件挂载到页面中。 -->
<div id="app">
<child-component></child-component>
<child-component></child-component>
<child-component></child-component>
</div>
- 效果图:
以上是一个Vue组件从创建到挂载到页面中的过程。其中可以省略掉第一步的创建过程,直接在第二步注册的时候自动执行创建。
- 语法:
Vue.component(id, [function | object | definition])
- 参数说明:
id
:字符串类型,组件名称。function
:可以传入函数Vue.extend({ /* ... */ })
;
传入一个选项对象 (自动调用 Vue.extend){ /* ... */ }
;
获取注册的组件 (始终返回构造器)Vue.component('my-component')
。
因此,上述Vue基础实例可以使用以下方法简化:
Vue.component("child-component", {
template: `<h2>Hello World</h2>`
});
实例 利用全局组件做一个点赞的小案例:
<div id="app">
<btn-inc></btn-inc>
<btn-inc></btn-inc>
<btn-inc></btn-inc>
</div>
<hr>
<template id="inc">
<div>
<button @click="count++">点赞:+{{count}}</button>
</div>
</template>
<script>
Vue.component("btn-inc", {
template: "#inc",
data() {
return {
count: 0
}
}
});
const vm = new Vue({
el: "#app"
});
</script>
- 效果图:
注意:
使用
Vue.component()
方法创建的组件是全局组件。全局组件必须声明在Vue实例的外部,并且必须声明在实例创建前。
1.1.2 局部组件
局部组件:需要在实例化对象中通过components属性进行注册。
1.第一种写法:直接写在组件里面的template
属性中。
const vm = new Vue({
// 局部组件是属于vue实例的。
components: {
"btn-inc": {
template: `<h2>Hello {{site}} </h2>`,
data () {
return {
site: "PHP中文网"
}
}
}
}
}).$mount("#app");
使用:
<div id="app">
<btn-inc></btn-inc>
<btn-inc></btn-inc>
<btn-inc></btn-inc>
</div>
2.在自定义html标签中渲染
<div id="app">
<btn-inc></btn-inc>
<btn-inc></btn-inc>
<btn-inc></btn-inc>
</div>
<template id="hello">
<div>
<h2>Hello {{site}} </h2>
</div>
</template>
const vm = new Vue({
components: {
"btn-inc": {
// 创建一个id为hello的自定义hmtl标签
template: "#hello",
data () {
return {
site: "PHP中文网"
}
}
}
}
}).$mount("#app");
3.给模板字面量赋给一个变量值
const hello = `<h2>Hello {{site}} </h2>`;
const vm = new Vue({
components: {
"btn-inc": {
template: hello,
data () {
return {
site: "PHP中文网"
}
}
}
}
}).$mount("#app");
使用:
<div id="app">
<btn-inc></btn-inc>
<btn-inc></btn-inc>
<btn-inc></btn-inc>
</div>
4.根据ES6新特性进行简化
现在,我把整个模板和它的data()函数全部拿出来做一个对象字面量付给一个变量hello:
let hello = {
template: `<h2>Hello {{site}}</h2>`,
data () {
return {
site: "PHP中文网"
}
}
};
然后,在vue实例的components组件中定义一个组件名也为hello的组件:
const vm = new Vue({
components: {
hello: hello
}
}).$mount("#app");
由于ES6有一个新特性,当对象的属性和值相同时,可简写,所以我还可以这样写:
const vm = new Vue({
components: {
hello,
}
}).$mount("#app");
使用:
<div id="app">
<hello></hello>
<hello></hello>
<hello></hello>
</div>
- 以上四种写法都可以实现相同的效果,如下图:
注意事项:
在使用子组件的时候,例如上述我们定义的<btn-inc>
组件,我们会发现他的data并不是一个对象:
data: {
site: "PHP中文网"
}
取而代之的是函数,也就是说一个组件的data选项
必须是一个函数,因此每个实例可以维护一份被返回对象的独立拷贝:
data () {
return {
site: "PHP中文网"
}
}
以上这一点是特别需要注意的。
二、组件之间的值传递
2.1 父组件向子组件的传值方式
父组件向子组件传值是通过props属性进行的,Prop是我们在组件上注册的一些自定义attitude。当一个值传递给一个prop attribute的时候,它就变成组件实例的一个property。为了给下面的user-info
组件传递用户id,用户名,用户邮箱
值,我们可以使用一个props数组将其包含在该组件可接收的prop列表中:
const vm = new Vue({
el: "#app",
data () {
return {
users: [
{ id: 1, name: "残破的蛋蛋", email: "canpo@dd.cn" },
{ id: 2, name: "拤碎的蛋蛋", email: "qiasui@dd.cn" },
{ id: 3, name: "漂亮的蛋蛋", email: "piaoliang@dd.cn" },
]
}
},
components: {
userInfo: {
props: ["selfId", "selfUsername", "selfEmail"],
template: `
<div>
<span>id:{{selfId}},</span>
<span>用户名:{{selfUsername}},</span>
<span>邮箱:{{selfEmail}}</span>
</div>
`
}
}
});
一个组件可以拥有任意数量的prop,任何值都可以传递给prop,在上述案例中,我们就可以在组件实例中访问到这个值,就跟直接访问data中的值一样。
一个组件被注册了之后,我们就可以这样把数据作为一个自定义的attribute传递进来:
<div id="app">
<user-info
v-for="user of users"
:self-username="user.name"
:self-id="user.id"
:self-email="user.email"
:key="user.id"
>
</user-info>
</div>
- 效果图:
2.2 子组件向父组件的传值方式
正常情况下,子组件向父组件传参的是无法直接传递的,需要用到Vue内建的$emit()方法传入事件名称来触发一个事件,
- 语法:
$emit(eventName, […args])
- 参数:
eventName:事件方法,字符串类型
[…args]*:子组件传递给父组件的参数值
以下是一个点赞的案例:首先声明一个Vue实例,并创建一个组件<btn-inc>
,由于子组件向父组件传值需要使用同名事件方法通过$emit()
进行传值,因此我们需要在子组件<button>
上添加一个点击事件方法@click="$emit('click-count', 10)
并传递一个click-count
方法和值。
<!-- 子组件 -->
<template id="btn">
<div>
<button @click="$emit('click-count', 10)">
点赞 + {{myCount}}
</button>
<span>{{myUsername}}</span>
</div>
</template>
父组件<btn-inc>
上需要监听一个点击事件click-count
:
<div id="app">
<btn-inc :my-count="count" :user-name="username" @click-count="handle"></btn-inc>
</div>
在Vue实例下创建一个handle方法来处理点击事件,这样我们就可以通过子组件向父组件传值了:
handle(value) {
this.count += value
}
完整的案例代码:
<div id="app">
<btn-inc :my-count="count" :user-name="username" @click-count="handle"></btn-inc>
</div>
<template id="btn">
<div>
<button @click="$emit('click-count', 10)">
点赞 + {{myCount}}
</button>
<span>{{myUsername}}</span>
</div>
</template>
<script>
const vm = new Vue({
data () {
return {
count: 0,
username: "残破的蛋蛋"
};
},
components: {
btnInc: {
props: ["userName", "myCount"],
template: "#btn",
}
},
methods: {
handle(value) {
console.log(this.count);
this.count += value
}
},
}).$mount("#app");
</script>
- 相关参数方法对应关系图:
- 效果图:
2.23 父子组件之间的双向传参
上面两个案例讲述了父组件向子组件传参以及子组件向父组件传参,下面这个案例讲展示父子组件之间如何双向传参。
<div id="app">
<!-- 父组件 -->
<p>父组件:{{price}} 元</p>
<my-input :my-price="price" @input-text="handle"></my-input>
</div>
<!-- 子组件 -->
<template id="myinput">
<div>
<input type="text" :value="myPrice" @input="$emit('input-text', $event.target.value)"> 元
</div>
</template>
<script>
const vm = new Vue({
data() {
return {
price: 4588
};
},
// 子组件向父组件传参时通过声明同名事件实现的
components: {
myInput: {
template: "#myinput",
props: ["my-price"],
}
},
methods: {
handle (value) {
this.price = value;
}
},
}).$mount("#app");
</script>
- 效果图:
不论我们是在输入框内改变子组件的值,还是在控制台中修改父组件的值,另外一个值都会随之而改变,这就是组件之间的双向传参。