准备
目录结构
cli服务 npm i @vue/cli-service-global -g
入口文件:main.js
import Vue from "vue";
import App from "./App"
new Vue({
el:"#app",
render:h=>h(App)
})
根组件:APP.Vue.js
<template>
<div>
<parent></parent>
</div>
</template>
<script>
import parent from "./components/parent"
export default{
components: {
parent
}
}
</script>
组件通信
1. props
父组件通过props
的方式向子组件传递数据,而通过$emit
子组件可以向父组件通信。
- 父传子
parent.vue
<template>
<div>
parent--->{{number}}
<hr />
<son :number = number @changeInParent="changeInParent"></son>
</div>
</template>
<script>
import son from "./son";
export default {
data() {
return {
number: 100
};
},
methods: {
changeInParent(newValue){
this.number = newValue
}
},
components: {
son
}
};
</script>
son.vue
<template>
<div>
<p>son--->{{number}}</p>
<button @click ="changeInSon()" >change</button>
<hr>
</div>
</template>
<script>
export default {
props: {
number: Number
},
methods: {
changeInSon() {
this.$emit("changeInParent",200)
}
},
};
</script>
2. $emit
- 父传子,子传孙
在上面的基础上加上grandson.vue
组件,parent.vue
不做修改
son.vue
<template>
<div>
<p>son--->{{number}}</p>
<button @click ="changeInSon()" >change</button>
<hr>
<grandson :number = "number" @change="change"></grandson>
</div>
</template>
<script>
import grandson from "./grandson"
export default {
props: {
number: Number
},
methods: {
changeInSon() {
this.$emit("changeInParent",200)
},
change(newValue){
this.$emit("changeInParent",newValue)
}
},
components:{
grandson
}
};
</script>
grandson.vue
<template>
<div>
<p>grandson--->{{number}}</p>
<button @click="changeInGrandson()">change</button>
</div>
</template>
<script>
export default {
props: {
number: Number
},
methods: {
changeInGrandson(){
this.$emit("change",300)
}
}
};
</script>
同步父子组件的数据=>语法糖的写法
2.1. .sync/update
parent.vue
<template>
<div>
parent--->{{number}}
<hr />
<son :number = number @update:number="changeInParent"></son>
</div>
</template>
<script>
import son from "./son";
export default {
data() {
return {
number: 100
};
},
methods: {
changeInParent(newValue){
this.number = newValue
}
},
components: {
son
}
};
</script>
</script>
以上还可以简化为:
parent.vue
<template>
<div>
parent--->{{number}}
<hr />
<!-- <son :number = number @update:number="newValue=>number = newValue"></son> -->
<son :number.sync = number></son>
</div>
</template>
<script>
import son from "./son";
export default {
data() {
return {
number: 100
};
},
components: {
son
}
};
</script>
son.vue
<template>
<div>
<p>son--->{{number}}</p>
<button @click ="changeInSon()" >change</button>
<hr>
</div>
</template>
<script>
export default {
props: {
number:Number,
},
methods: {
changeInSon() {
this.$emit("update:number",600)
},
},
};
</script>
2.2 .v-model/input/value
v-mode
只能给子组件传递一个属性
parent.vue
<template>
<div>
parent--->{{number}}
<hr />
<!-- <son :value = number @input="newValue => number = newValue"></son> -->
<son v-model="number"></son>
</div>
</template>
<script>
import son from "./son";
export default {
data() {
return {
number: 100
};
},
components: {
son
}
};
</script>
son.vue
<template>
<div>
<p>son--->{{value}}</p>
<button @click ="changeInSon()" >change</button>
<hr>
</div>
</template>
<script>
export default {
props: {
value: Number
},
methods: {
changeInSon() {
this.$emit("input",700)
},
},
};
</script>
3. $children/$parent
使用 this.$parent
查找当前组件的父组件,可直接触发父组件的父组件的方法
使用 this.$children
查找当前组件的直接子组件,可以遍历全部子组件,但$children
并不保证顺序,也不是响应式的。
parent.vue
<template>
<div>
parent--->{{number}}
<hr />
<son :number = number @changeInParent="changeInParent"></son>
</div>
</template>
<script>
import son from "./son";
export default {
data() {
return {
number: 100
};
},
methods: {
changeInParent(newValue){
this.number = newValue
}
},
components: {
son
}
};
</script>
son.vue
<template>
<div>
<p>son--->{{number}}</p>
<button @click ="changeInSon()" >change</button>
<hr>
<grandson :number = "number"></grandson>
</div>
</template>
<script>
import grandson from "./grandson"
export default {
props: {
number: Number
},
methods: {
changeInSon() {
this.$emit("changeInParent",200)
},
},
components:{
grandson
}
};
</script>
grandson.vue
<template>
<div>
<p>grandson--->{{number}}</p>
<button @click="changeInGrandson()">change</button>
</div>
</template>
<script>
export default {
props: {
number: Number
},
methods: {
changeInGrandson(){
this.$parent.$emit("changeInParent",400)
}
}
};
</script>
出现多层时使用 :$disptch/broadcast
如果层级很深就会出现$parent.$parent
…这里封装一个$dispach
方法进行向上派发
parent.vue
和son.vue
较上一个案例不做修改,
在main.js
中定义一个dispatch
方法,$dispatch
可以逐级往上找父中的方法,
在main.js
中定义一个broadcast
方法,broadcast
可以逐级往上找父中的方法.
main.js
import Vue from "vue";
import App from "./App"
// 自己定义一个disptch方法
Vue.prototype.$dispatch = function(evenName,value){
let parent = this.$parent;
while(parent){
parent.$emit(evenName,value)
parent = parent.$parent
}
}
new Vue({
el:"#app",
render:h=>h(App)
})
grandson.vue
<template>
<div>
<p>grandson--->{{number}}</p>
<button @click="changeInGrandson()">change</button>
</div>
</template>
<script>
export default {
props: {
number: Number
},
methods: {
changeInGrandson(){
this.$dispatch("changeInParent",500)
}
}
};
</script>
4.$attrs、 $listeners
$attrs、 $listeners
均可跨级传值
$attrs
用来接收传过来的多个属性,跨级传用 v-bind
$listeners
用来接收传过来的多个方法,跨级传用 v-on
parent.vue
<template>
<div>
parent--->{{number}}--->{{sum}}
<hr />
<son :number="number" :sum="sum" @click="showclick" @mousedown="showmouse"></son>
</div>
</template>
<script>
import son from "./son";
export default {
data() {
return {
number: 100,
sum: 0
};
},
methods: {
showclick() {
window.console.log("clickEvent...");
},
showmouse(){
window.console.log("mouseEvent...");
}
},
components: {
son
}
};
</script>
son.vue
<template>
<div>
<p>son--->{{$attrs.number}}--->{{$attrs.sum}}</p>
<hr>
<grandson v-bind="$attrs" v-on = "$listeners" ></grandson>
</div>
</template>
<script>
import grandson from "./grandson"
export default {
inheritAttrs: false, //可以把传递过来的属性不作为标签真正的属性
components:{
grandson
},
};
</script>
grandson.vue
<template>
<div>
<p>grandson--->{{$attrs}}</p>
<p>grandson--->number:{{$attrs.number}}--->{{$attrs.sum}}</p>
<button @click="$listeners.click()">triggerClickEvent</button>
<br><br>
<button @click="$listeners.mousedown()">triggerMouseEvent</button>
</div>
</template>
5. Provide&Inject
数据比较多的情况下,不建议使用
Provide
在父级中提供数据
<template>
<div>
<parent></parent>
</div>
</template>
<script>
import parent from "./components/parent";
export default {
provide() {
return {
name: "wangcai"
};
},
components: {
parent
}
};
</script>
Inject
在任意子组件中可以注入父级数据
<template>
<div>
{{name}}
</div>
</template>
<script>
export default {
inject: ["name"],
};
</script>
6. ref/refs
获取组件实例,在父组件中得到子组件,并且调用子组件的方法
利用ref
给子组件起一个name
,然后在父组件中利用this.$refs.name.alertINfo()
去获取子组件中的alertINfo()
方法
parent.vue
<template>
<div>
<son ref="sonMethods"></son>
</div>
</template>
<script>
import son from "./son";
export default {
mounted() {
this.$refs.sonMethods.alertINfo()
},
components: {
son
}
};
</script>
son.vue
<template>
<div>
</div>
</template>
<script>
export default {
methods: {
alertINfo() {
window.console.log("this is a methods on son ......");
}
}
};
</script>
7. EventBus
事件总线,用于跨组件通知
在入口文件中将$bus
挂载到Vue原型上
然后利用this.$bus.$on("event",function(){ })
在父组件注册事件
然后利用this.$bus.$emit("event")
在子组件注册事件
main.js
import Vue from "vue";
import App from "./App";
// 将$bus挂载到Vue原型上面的一个vm
Vue.prototype.$bus = new Vue()
new Vue({
el: "#app",
render: h => h(App)
});
parent.vue
<template>
<div>
<son></son>
</div>
</template>
<script>
import son from "./son";
export default {
mounted() {
this.$bus.$on("change",function(){
window.console.log("change...");
})
},
components: {
son
}
};
</script>
grandson.vue
<template>
<div>
</div>
</template>
<script>
export default {
mounted(){
this.$nextTick(()=>{
this.$bus.$emit("change")
})
}
};
</script>