组件:可复用的 Vue 实例,且带有一个名字,并且接受相同的选项对象,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样的根实例特有的选项。
作用:可以将组件进行任意次数的复用。每个组件都会各自独立维护它自己。因为每用一次组件,就会有一个它的新实例被创建。
组件的模板只能有一个根元素。
//以下情况是不允许的
Vue.component('my-component', {
template: `<div>这是一个局部的自定义组件,只能在当前Vue实例中使用</div>
<button>hello</button>`
})
组件中的data必须是一个函数?
由于JS中对象类型的变量实际上保存的是对象的引用,所以当存在多个这样的组件时,会共享数据,导致一个组件中数据的改变会引起其他组件数据的改变。
而使用一个返回对象的函数,每次使用组件都会创建一个新的对象,这样就不会出现共享数据的问题了。
总结:通过函数生成了不同的对象, 属性的改变不会再互相影响
Vue.component('my-component', {
data:function(){
return {
name:'Lee‘
}
},
})
组件名:
使用kebab-case(短横线分割命名):当使用短横线分割命名定义一个组件时,在引用这个自定义元素时也必须使用这种方式。
Vue.component('my-component-name', { /* ... */ })
使用PascalCase(首字母大写命名):当使用首字母大写命名定义一个组件时,在引用这个自定义元素时两种命名法都可以使用。
Vue.component('MyComponentName', { /* ... */ })
全局组件的定义
component----组件(就是对象)
Vue.component("mynav",{
template:"<ul><li>首页</li><li>我的</li></ul>"
})
注意:在定义组件时里面的data属性必须是函数式编程,目的是每次调用组件返回一个独立的数。
局部组件的定义
//1.创建组件
//局部注册的组件只能在当前的Vue实例中使用。
let mynav={
template:"<p>这是一个导航</p>",
data(){
return{
}
},
methods:{},
watch:{},
computed:{},
}
//2.注册组件
const app=new Vue({
el:'#app',
components:{
mynav
}
})
*注意:驼峰命名组件时,使用组件写标签形式的时候该用-连接,例如定义了一个组件时myIpt,那么使用标签就应该这样写<my-ipt></my-ipt>
局部组件component的用方法
我们来看用法:
<!-- 动态组件由 vm 实例的 `componentId` property 控制 -->
<component :is="componentId"></component>
我们来看例子:
<body>
<div id="app">
<!-- 组件的另外一种引入方式 -->
<component :is="current"></component>
</div>
<script src="../../node_modules/vue/dist/vue.js"></script>
<script>
let home = {
template: '<div>home</div>'
}
let about = {
template: '<div>about</div>'
}
const app = new Vue({
el: '#app',
data: {
current: about
},
components: {
home, about
}
})
</script>
</body>
组件的嵌套
<body>
<div id="app">
<father></father>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let son = {
template: '<div>son</div>'
}
let father = {
template: '<span>father<son></son></span>',
components: {
son
}
}
const app = new Vue({
el: '#app',
components: {
father
}
})
</script>
</body>
注意:组件的嵌套子组件声明要放在父组件前面,父组件内必须声明子组件
组件间的通信
父向子通讯
父组件通过Props向子组件传递数据:
props的使用,首先在父组件的子标签内写上要传递的元素,接着在子组件中通过props来接收,相当于一个data值,不过注意这里不能够改变该值,因为是父组件给的。
在子组件中使用 props 接收父组件传递的数据,props 里的名字跟父组件定义的属性名一致
但是子组件内想要修改父组件传过来的值却不“污染”父组件的话,可以在子组件内定义一个变量mydata去接收fatherData数据,并使用 watch 监听fatherData数据的变更。
Vue子组件通过props属性来接受父组件传递过来的数据。
由于HTML特性是不区分大小写的,所以传递属性值时,要用短横线分割命名。
<body>
<div id="app">
<father></father>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let son = {
template: '<div>son {{m}}</div>',
// props: ['m'], //props:两种方法书写,对象和数组的方式
props:{
m:{
// type:Number, //规定该值的类型必须是什么
// default:300, //默认值
required:true //父组件是否必须写这个m属性
}
}
}
let father = {
template: '<span>father<son :m="money" ></son></span>',
components: {
son
},
data(){
return{
money:100
}
}
}
const app = new Vue({
el: '#app',
components: {
father
}
})
</script>
</body>
子向父通讯
使用自定义事件
//子组件发布事件
this.$emit('more') 发布事件
//父组件注册事件
@more='handler()' //handler是一个函数体
我们来看详细代码:
<body>
<div id="app">
<father></father>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let son = {
template: '<div>son {{m}} <button @click="add">点击增加</button> </div>',
// props: ['m'],
props:{
m:{
required:true
}
},
methods: {
add(){
this.$emit('add') //发布一个add
}
},
}
let father = {
template: '<span>father<son :m="money" @add="add"></son></span>', //son标签内接收add并触发后面的函数。
components: {
son
},
data(){
return{
money:100
}
},
methods:{
add(){
this.money+=100
}
}
}
const app = new Vue({
el: '#app',
components: {
father
}
})
</script>
</body>
兄弟组件之间的通信
创建一个事件车的Vue实例,通过它来发布和订阅。
<body>
<div id="app">
<brother1></brother1>
<bro-ther2></bro-ther2> //注意驼峰命名的组件标签的写法
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
const eventBus = new Vue()
let brother1 = {
template: '<span @click="changecolor">111</span>',
methods:{
changecolor(){
eventBus.$emit('green') //通过事件车去发布green
}
}
}
let broTher2 = {
template: '<span :style={color:color}>222</span>',
created(){
eventBus.$on('green',()=>{ //通过事件车订阅了green事件,并执行回调函数赋值color
this.color='green'
})
},
data(){
return{
color:''
}
}
}
const app = new Vue({
el: '#app',
components: {
brother1,
broTher2
}
})
</script>
</body>
隔代通讯
使用的方法:provide inject
<body>
<div id="app">
<grand-father></grand-father>
</div>
<!-- 各个模板的书写 -->
<template id="son">
<div>
Son
{{msg}}
</div>
</template>
<template id="father">
<div>
Father
<son></son>
</div>
</template>
<template id="grandFather">
<div>
grandFather
<father></father>
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let son = {
template: '#son',
inject:['msg'] //inject接收
}
let father = {
template: '#father',
components: {
son
}
}
let grandFather = {
template: '#grandFather',
components: {
father
},
provide:{
msg:"隔代通讯" //provide 提供
}
}
const app = new Vue({
el: '#app',
components: {
grandFather
}
})
</script>
</body>
PubSub插件实现 任意组件间的通讯
同样的可以用第三方插件:PubSub来实现同样的操作
pubsub.subscribe() 订阅
pubsub.publish() 发布
<body>
<div id="app">
<brother1></brother1>
<brother2></brother2>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="https://unpkg.com/PubSub"></script>
<script>
const eventBus = new Vue();
const pubsub = new PubSub();
let brother1 = {
template: '<span @click="changeGreen" :style={color:color}>brother1</span>',
methods: {
changeGreen() {
pubsub.publish('green')
}
},
data() {
return {
color: ''
}
},
created() {
pubsub.subscribe('red', () => {
this.color = 'red'
})
}
}
let brother2 = {
template: '<span :style={color:color} @click="changeRed">brother2</span>',
created() {
pubsub.subscribe('green', () => {
console.log(111);
this.color = 'green';
})
},
data() {
return {
color: ''
}
},
methods: {
changeRed() {
pubsub.publish('red')
}
}
}
const app = new Vue({
el: '#app',
components: {
brother1,
brother2
}
})
</script>
</body>
父子组件的互相访问
父访问子
ref方法的使用,利用ref绑定组件,获得绑定的组件。
<body>
<div id="app">
<father></father>
</div>
<!-- 各个模板的书写 -->
<template id="son">
<div v-show='isShow'>
Son
</div>
</template>
<template id="father">
<div>
Father
<son ref="myson"></son>
<button @click="change">显示或隐藏</button>
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let son={
template:'#son',
methods: {
changeColor(){
return this.isShow=!this.isShow
}
},
data(){
return{
isShow:false
}
}
}
//ref 加到组件上的时候,获取到的是Vue实例
let father={
template:'#father',
components:{
son
},
methods:{
change(){
console.log(this.$refs.myson)
this.$refs.myson.changeColor()
}
}
}
const app=new Vue({
el:'#app',
components:{
father
}
})
</script>
</body>
子访问父
$parent方法,访问到父组件。
<body>
<div id="app">
<father></father>
</div>
<!-- 各个模板的书写 -->
<template id="son">
<div >
Son
<button @click="get">点击我得到父级元素的属性</button>
</div>
</template>
<template id="father">
<div>
Father
<son></son>
</div>
</template>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
let son = {
template: '#son',
methods: {
get(){
console.log(this.$parent.money) //$parent 访问父组件
}
},
}
let father = {
template: '#father',
components: {
son
},
data(){
return{
money:111234
}
}
}
const app = new Vue({
el: '#app',
components: {
father
}
})
</script>
</body>
总结:
常见使用场景可以分为三类:
父子通信: 父向子传递数据是通过 props,子向父是通过 events( $emit);通过父链 / 子链也可以通信( $parent / $children);ref 也可以访问组件实例;provide / inject API; $attrs/$listeners
兄弟通信: Bus;Vuex
跨级通信: Bus;Vuex;provide / inject API、 $attrs/$listeners