目录
一、组件介绍
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用is特性进行了扩展的原生 HTML 元素。组件注册的时候需要为该组件指定各种参数。
因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
组件的特点:
- 组件可以进行任意次数的复用
- 组件的data必须是一个函数,确保每个实例可以维护一份被返回对象的独立拷贝,也就是任何一个组件的改变不会影响到其他组件
1、定义组件
组件与new Vue有相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等,但是组件没有el选项,而是使用template
let myA={
template:`
<div>
{{msgA}}
</div>
`,
data(){
return {
msgA:'我是子组件'
}
}
};
2、注册组件
使用组件之前需要先注册组件,注册组件的方式有:全局注册和局部注册
2.1 全局注册
使用Vue.component('组件名', 组件); 进行全局注册,全局注册之后的组件可以在任意组件里面使用。
Vue.component('my-a',myA);
2.2 局部注册
局部注册的组件只能在当前组件中使用,所以我们想在哪个组件中使用该组件,就在哪个组件中局部注册该组件。我们使用components配置项进行局部注册。
比如我们想要在myB组件中使用myA组件,那么我们就需要在myB组件里面对myA组件进行局部注册:
let myA={
template:`
<div>
{{msgA}}
</div>
`,
data(){
return {
msgA:'我是子组件'
}
}
};
let myB={
// 局部注册
components:{
//组件名: 组件
'my-a':myA
},
template:`
<div>B组件
<my-a></my-a>
</div>
`
}
3、使用组件
使用组件只需要在模板中使用html标签,标签名是组件名:
<div id="app">
<my-a></my-a>
<my-b></my-b>
</div>
二、组件通信方式
组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。在 Vue 中,父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
1、父组件给子组件传值
父组件给子组件传值的方式:
第一步:在子组件的标签内写入需要传递的值
第二步:在子组件内使用props配置项接收父组件传递过来的值
<body>
<div id="app">
<!-- 父组件传值给子组件 -->
<!-- 1.在子组件标签上 写入传入的值 -->
<!-- 静态传参 不使用v-bind进行数据绑定,那么传递的就是'msg'字符串本身-->
<!-- <my-a msg1="msg"></my-a> -->
<!-- 动态传参 使用v-bind进行数据绑定,就可以传递任意类型的值-->
<my-a :msg2="msg" :age="18" :obj="{name:'zhangsan'}" :stu='[1,2,3]'></my-a>
</div>
<script>
let myA={
// 子组件内使用props接收父组件传递的值
props:['msg2','stu','age','obj'],
template:`
<div>
我是子组件<br>
{{msg2}}---{{typeof msg2}}<br>
{{stu}}---{{typeof stu}}<br>
{{age}}---{{typeof age}}<br>
{{obj}}---{{typeof obj}}
</div>
`
};
new Vue({
// 局部注册组件
components:{
'my-a':myA
},
el:"#app",
data:{
msg:'我是父组件'
}
})
</script>
</body>
在props中,子组件可以对父组件的传值进行类型校验,当传入的值不是我们想要的类型的时候,会进行报错,此时props需要写成对象的形式:
注意:null和undefined可以通过任意类型的校验
<body>
<div id="app">
<!-- 父组件传值给子组件 -->
<!-- 1.在子组件标签上 写入传入的值 -->
<!-- 静态传参 不使用v-bind进行数据绑定,那么传递的就是'msg'字符串本身-->
<!-- <my-a msg1="msg"></my-a> -->
<!-- 动态传参 使用v-bind进行数据绑定,就可以传递任意类型的值-->
<my-a :msg2="msg" :age="18" :obj="{name:'zhangsan'}" :stu='[1,2,3]' :stu2="19"></my-a>
</div>
<script>
let myA = {
// 子组件内使用props接收父组件传递的值
props: {
age: {
// 可以进行多个类型值的校验
// 可以是Number,String,Boolean类型中的任意一个
type: [Number, String, Boolean],
// 自定义校验器规则 参数val表示传过来的值
validator(val) {
return val > 50
}
},
msg2: String,
obj: Object,
stu2: String,
stu: {
type: Array,
// 可以设置默认值,注意:需要写成函数的形式
default () {
return [4, 5, 6]
},
//要求必须传递
required: true
}
},
template: `
<div>
我是子组件<br>
{{msg2}}---{{typeof msg2}}<br>
{{stu}}---{{typeof stu}}<br>
{{stu2}}---{{typeof stu2}}<br>
{{age}}---{{typeof age}}<br>
{{obj}}---{{typeof obj}}
</div>
`
};
new Vue({
// 局部注册组件
components: {
'my-a': myA
},
el: "#app",
data: {
msg: '我是父组件'
}
})
</script>
</body>
2、子组件给父组件传值
子组件给父组件传值的方式:
第一步:在子组件中使用 $emit('发射事件的名称',传递的参数) 给父组件发射自定义事件和传递值
第二步:在父组件中声明自定义事件来接收参数
<body>
<div id="app">
<!-- 自定义事件 -->
<my-a @my-event="handler"></my-a>
</div>
<script>
let myA = {
template: `
<div>我是子组件
<button @click='toSend'>子传值给父</button>
</div>
`,
data() {
return {
subMsg: "我是子组件A,我要给父组件传值"
}
},
methods: {
toSend() {
// 发射事件 $emit('发射事件的名称',传递的参数)
this.$emit('my-event', this.subMsg)
}
}
}
new Vue({
components: {
'my-a': myA
},
el: '#app',
data: {
msg: "我是父组件",
},
methods: {
handler(a) {
console.log(a, '这是子组件的值');
}
}
})
</script>
</body>
3、兄弟组件之间的传值
兄弟组件之间传值的方式:
使用事件总线:
第一步:声明一个事件总线xxx.js,即新建一个js文件,在js文件导出一个Vue实例
第二步:引入事件总线xxx.js,即在其中一个兄弟组件内导入xxx.js文件,并使用$emit()将数据发射出去
第三步:在另一个兄弟组件导入xxx.js文件,并使用$on()接收兄弟组件传递过来的数据
我们在vue脚手架中演示:
创建好脚手架后,我们在src下的components文件夹下建立两个.vue文件,分别是Header.vue和Footer.vue,然后在App.vue中引入这两个组件并使用,此时Header.vue和Footer.vue就是兄弟组件,然后在src下新建一个eventBus.js文件,它就是事件总线。
App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Header/>
<hr>
<Footer/>
</div>
</template>
<script>
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
Header,
Footer,
},
data() {
return {
msg:'我是祖先组件'
}
},
provide(){
return {'msg':this.msg}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
eventBus.js
import Vue from 'vue';
export default new Vue();
Header.vue
<template>
<div>
{{msgH}}
<button @click="handler">点击我传值给Footer</button>
</div>
</template>
<script>
// 引入事件总线
import Bus from '../eventBus.js'
export default {
data(){
return {
msgH:"头部组件",
Footer:"这是兄弟组件Footer"
}
},
methods:{
handler(){
// 兄弟组件传值
// 1.兄弟组件使用$emit('发射事件',携带参数)
Bus.$emit('toFoot',this.Footer)
}
}
}
</script>
Footer.vue
<template>
<div>
{{msgF}}
</div>
</template>
<script>
// 引入事件总线
import Bus from '../eventBus.js'
export default {
data(){
return {
msgF:"底部组件",
}
},
// 另一个兄弟组件使用 $on(自定义事件名称,(形参 用于接收兄弟组件传递过来的值)=>{}) 接收参数
created(){
Bus.$on('toFoot',(a)=>{
console.log(a,"这是Footer传给我的值");
})
}
}
</script>
4、祖先组件和子孙组件之间的传值
祖先组件和子孙组件之间的传值方式:
第一步:祖先组件使用provide方法传递数据
第二步:子孙组件使用inject注入数据
我们同样在vue脚手架中演示:
在components文件夹下再新建一个FooterChild.vue文件,然后在Footer.vue组件中使用FooterChild.vue组件,App.vue中使用Footer.vue组件,那么App.vue就作为祖先组件,FooterChild.vue就作为子孙组件了。
App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<Header/>
<hr>
<Footer/>
</div>
</template>
<script>
import Header from './components/Header.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
Header,
Footer,
},
data(){
return {
msg: '我是祖先组件',
a: 18
}
},
// 祖先组件使用provide方法提供传递的数据
provide(){
return {
"msg":this.msg,
"a":this.a
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Footer.vue
<template>
<div>
{{msgF}}
<FooterChild/>
</div>
</template>
<script>
import FooterChild from './FooterChild.vue'
export default {
components:{
FooterChild
},
data(){
return {
msgF:"底部组件",
}
}
}
</script>
FooterChild.vue
<template>
<div>
{{msgC}}--{{msg}}--{{a}}
</div>
</template>
<script>
export default {
data(){
return {
msgC:"底部组件的子组件"
}
},
// 注入数据
inject:['msg','a']
}
</script>
5、关于单向数据流
单向数据流是指数据是由上层向下层流动的,也就是说父组件的数据发生改变后,子组件的数据也会发生改变;而子组件的数据发生改变,父组件的数据不会受到影响。