组件通信:
值得强调一点的是 无论组件通信结果如何,Vue都是单向数据流(组件之间的数据通信)。
1.父子通信:
绑定的是简单数据:
1.父组件中定义数据,通过单向数据绑定的形式,将数据绑定在子组件上,属性是自定义属性
2.子组件通过配置项中的props接受数据,props可以是一个数组,数组中放的是自定义属性名称
3.那么这个自定义属性可以向data中的数据一样直接在子组件的模板中使用。
4.父组件中的数据一旦发生改变,子组件中的数据也会跟着改变
举个栗子:
<div id="app">
<father></father>
</div>
<template id="father">
<div class="father-box">
<h3>父组件</h3>
<son v-bind:money = "money"></son>
<son :money = "money"></son>
</div>
</template>
<template id="son">
<div class="son-box">
<h3>子组件</h3>
<p> {{ money }} </p>
</div>
</template>
<script>
Vue.component('father', {
template : "#father",
data (){
return {
money : 100
}
}
})
Vue.component('son', {
template : "#son",
props : [money]
})
new Vue({
el: "#app",
})
</script>
子父通信:
效果上间接使用的父组件的东西。
绑定复杂数据类型:
1.父组件中的数据是一个复杂数据类型,那么父组件绑定数据的时候,给子组件一个地址。
2.子组件可以通过这个地址来修改这个数据。
3.在效果上,子组件和父组件建立了通信,违背了单向数据流。
多说无益:
<div class="app">
<father></father>
</div>
<template id="father">
<div class="father-box">
<p>父组件 {{ setmoney.money }}</p>
<son v-bind:setmoney="setmoney"></son>
</div>
</template>
<template id="son">
<div class="son-box">
<input type ="text" v-model = "setmoney.money"/>
<p>子组件 {{ setmoney.money }}</p>
</div>
</template>
<script>
Vue.component('father', {
template : '#father',
data () {
return {
setmoney : {
money : 100
}
}
}
})
Vue.component('son', {
template : '#son',
props : [setmoney]
})
new Vue({
el : '.app'
})
</script>
除了这种传递对象的方式,父组件还可以传递一个方法给子组件.
1.父组件定义方法,然后将这个方法通过单向数据绑定的形式传递给子组件
2.子组件通过props属性接受,然后通过@click="方法名"
举例:
<div class="app">
<father></father>
</div>
<template id="father">
<div>
<h3>father</h3>
<p> {{ money }} </p>
<son :getmoney="getmoney"></son>
</div>
</template>
<template id="son">
<div>
<h3>son</h3>
<button @click="getmoney(100)">添加</button>
</div>
</template>
<script>
Vue.component('father', {
template : '#father',
data () {
return {
money : 0
}
},
methods : {
getmoney (value){
this.money += value
}
}
})
Vue.component('son', {
template : '#son',
props : ['getmoney']
})
new Vue ({
el : '.app'
})
</script>
同自定义事件来实现通信:
--父组件上定义数据 和方法
--在子组件上绑定自定义事件
--子组件定义方法,在这个方法中通过 this.$emit(eventtype,实际参数)来调用
自定义事件的处理程序
<div id="app">
<King></King>
</div>
<template id="king">
<div>
<h3> I am king </h3>
<!-- <People v-on: get = "get"></People> -->
<p> 国库中有:{{ gk }} 钱 </p>
<People @get = "get"></People>
</div>
</template>
<template id="people">
<div>
<h3> I am people </h3>
<button @click = "give"> 交钱 </button>
</div>
</template>
<script>
Vue.component('King',{
template: '#king',
data () {
return {
gk: 0
}
},
methods: {
get( value ){
this.gk += value
}
}
})
Vue.component('people',{
template: '#people',
data () {
return {
money: 2000
}
},
methods: {
give () {
this.$emit('get', this.money/2)
}
}
})
new Vue({
el: '#app'
})
</script>
非父子通信:
1.ref:
使用ref来绑定组件(ref也可以绑定DOM元素)
---在父级的模板中,使用ref = refName绑定在两个兄弟组件上。
---在任意一个子组件中,就可以通过this.$parent.$refs.refName就可以获取另一个组件,
同时这个组件上的数据和方法都可以获取到。
<div id="app">
<Father></Father>
</div>
<template id="father">
<div>
<h3> 我是父亲 </h3>
<hr>
<Boy ref = "boy" ></Boy>
<hr>
<Girl ref = "girl" ></Girl>
<div ref = 'hello'>
你好
</div>
</div>
</template>
<template id="boy">
<div>
<h3> I am boy </h3>
<p> 我现在有:{{ cash }} 钱 </p>
</div>
</template>
<template id="girl">
<div>
<h3> I am girl </h3>
<button @click = 'give'> 给body 1000块 </button>
</div>
</template>
<script>
Vue.component('Father',{
template: '#father',
data () {
return {
name: 'father'
}
}
})
Vue.component('Boy',{
template: '#boy',
data () {
return {
cash: 2000
}
},
methods: {
incrementCash (value) {
this.cash += value
}
}
})
Vue.component('Girl',{
template: '#girl',
data () {
return {
money: 1000
}
},
methods: {
give(){
// console.log('====================================');
// console.log(this.$parent);
// console.log('====================================');
// console.log( this.$parent.$refs ) // { body: Vuecomponent,girl: VueComponent }
this.$parent.$refs.boy.incrementCash( this.money )
// this.$parent.$children
}
}
})
new Vue({
el: '#app'
})
</script>
2.通过事件总线bus
他是通过事件的发布(声明),以及事件的订阅(触发)来实行的。
首先创建一个bus对象:
---- var bus = new Vue()
--在content组件中定义数据,和修改数据的方法
--在content组件中通过created钩子,进行bus事件的发布
----created : {
bus.$on('add', this.addcontent);
}
--- 在MyButton组件的方法中通过bus进行事件的订阅
----iincrement() {
bus.$emit( 'add' );
}
<div id="app">
<my-button></my-button>
<Count></Count>
</div>
<template id="button">
<button @click = "increment"> increment </button>
</template>
<template id="count">
<div>
{{ count }}
</div>
</template>
<script>
var bus = new Vue()
Vue.component('MyButton',{
template: '#button',
methods: {
increment () {
bus.$emit('add')
}
}
})
Vue.component('Count',{
template: '#count',
data () {
return {
count: 0
}
},
methods: {
addCount(){
this.count ++
}
},
created () { //created表示组件在创建结束之后就会自动触发的一个方法
//表示组件的装载结束(template模板的装载)
bus.$on('add',this.addCount)
}
})
new Vue({
el: '#app'
})
</script>
组件生命周期:
钩子函数,就是options中的key,他的值是函数。
钩子函数写在其他函数的后面。
1.组件有哪几个阶段:
初始阶段
运行中阶段。
销毁阶段
2.初始化阶段:
1.分为两个阶段:
每个阶段包含两个生命周期钩子函数:
钩子函数:
1.beforeCreate:
表示组件创建前的准备工作,为事件的发布订阅和生命周期做初始化。
在这个钩子函数中:
数据拿不到,真实DOM也拿不到。
这个钩子函数在项目中没有什么实际用途。
2.created :
表示组件创建结束。
这个钩子函数中:
数据能够拿到,但真实DOM无拿到。
这个钩子函数在项目中:
数据请求,然后可以进行一次默认的数据修改。
3.beforeMounte:
表示组件装在前的准备操作
判断el选项有没有,判断template有没有,如果没有需要手动装载。
如果有,那么利用render函数进行模板的渲染。
这个钩子函数中:
数据拿到了,但真实DOM没有拿到。
这个钩子函数在项目中:
数据请求,可以进行一次数据的修改。
4.mounted :
表示组件装载结束,可以在视图中看到。
这个钩子函数:
数据能够拿到,真实DOM也能拿到。
这个钩子函数在项目中:
DOM操作可以进行,第三方库的实例化。
总结:
由上对比,我们可以知道,数据请求越提前越好,所以created常用于数据的请求和数据的修改,第三方库的实例化常在mounted中进行。
运行中阶段:
----运行中阶段一共有两个钩子函数:
beforeupdate
---表示数据更新前的准备工作。
---这个钩子不会主动执行,只有在数据更新的后,才会执行。
---这个钩子函数中:
-----数据拿到了,并且拿到的是修改后的数据
-----DOM也输出了
--这个钩子函数更多的工作内容为:生成新的VDOM,然后通过diff算法进行两次VDOM对比。
--这个钩子函数在项目中:
-----因为他主要做的事情是内部进行的, 所以对我们而言没有太多的操作意义
updated:
--表示数据结束更新,通过render函数渲染真实DOM
--这个钩子函数的执行也是, 当数据修改的时候才执行
--这个钩子函数中
---数据拿到了, DOM也拿到了
--这个钩子在项目中
---也是进行第三方库的实例化( DOM是改变的 )
- 总结: 数据更新, 也要进行DOM操作那么, 我们使用update这个钩子
销毁阶段:
1.有两种销毁方式:
第一种:
用过开关销毁:
---这个组件真实DOM也会被删除掉
第二种:
通过调用vm.$destroy()
---这个组件的被删除了, 但是它的真实DOM的html结构还在
包含两个钩子函数
---beforeDestroy
---destroyed
---这两个钩子无差别
---这两个钩子在项目中
----做善后工作 , 手动清除一些计时器, 和一些方法, 还有第三方实例化出来的对象
我们建议大家使用开关的形式来操作组件的销毁和创建
举例:
<div id="app">
<button @click = 'flag = !flag'> 销毁 </button>
<life-circle v-if = "flag"></life-circle>
</div>
<template id="life-circle">
<div>
<h3> 销毁阶段 </h3>
<button @click = 'destroy'> 销毁 </button>
</div>
</template>
<script>
Vue.component('LifeCircle', {
template : '#lifr-circle',
methods : {
destory () {
this.$destory();
}
},
created () {
this.time = setInterVal(() =>{
console.log(1)
},1000)
},
beforeDestory () {
console.log('beforeDestory');
},
destory () {
console.log('destroyed');
clearInterval( this.timer )
document.querySelector('#app div').remove();
}
})
new Vue({
el: '#app',
data: {
flag: true
}
})
</script>