props
和$emit
父组件向子组件传递数据是通过prop
传递的,子组件传递数据给父组件是通过$emit
触发事件来做到的.sync
和v-model
的使用(父子组件数据同步)$parent
,$children
多层级传递数据,智能组件封装$attrs
(属性的集合)和$listeners
(方法的集合)。Vue 2.4 开始提供了$attrs
和$listeners
来解决A->B->C问题,组件间向下传递,可以不用props
注册。v-bind
属性传递,v-on
方法传递- 父组件中通过
provider
来提供变量,然后在子组件中通过inject
来注入变量。 $refs
获取实例envetBus
平级组件数据传递 这种情况下可以使用中央事件总线的方式
props传递数据
父组件
<template>
<div>
父组件:{{money}}
<son1 :money='money' @addMoney = 'addMoney'></son1>
</div>
</template>
<script>
import son1 from './son1'
export default {
components:{
son1
},
data(){
return {
money:100
}
},
methods:{
addMoney(val){
this.money = val
}
}
}
</script>
子组件 son1.vue
<template>
<div>
儿子1获得{{money}}
<button @click="raiseMoney">多给点嘛</button>
</div>
</template>
<script>
export default {
props:{
money:{
type:Number,
default:1
}
},
methods:{
raiseMoney(){
this.$emit('addMoney',200)
}
}
}
</script>
.sync 和 v-model的使用(父子组件数据同步)
.sync
- 需求需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。
- 以
update:myPropName
的模式触发事件取而代之 - 即
父组件 <son1 :money.sync='money'></son1>
子组件 this.$emit('update:money',200)
是对下面代码的简写(语法糖)父组件 <son1 :money="money" @update:money="(val)=>this.money=val"></son1>
子组件this.$emit('update:money',200)
v-model
<son1 :value="money" @input="(val)=>this.money=val></son1>
- 也可以写成
<son1 v-model="money"></son1>
- v-model这种方法,子组件只能接收
value
,具有局限性,而.sync
则可以随意起名字
多层级传递数据 $parent
$children
- 孙子组件直接更改父组件
<button @click="$parent.$emit('addMoney',400)">更改父组件</button>
像这样如果有多级孙子组件,就会一直$parent
下去,这样会很麻烦,不如直接封装一个$dispatch
$dispatch
只会通知自己对父亲, 而eventBus
是全局通知,通知所有的父亲或者儿子 - 在main.vue里封装
dispatch
向上通知
Vue.prototype.$dispatch = function (eventName,value){
let parent = this.$parent;
while(parent){
parent.$emit(eventName,value);
parent = parent.$parent
}
}
使用时只需要 this.$dispatch('addMoney',400)
//666 亲测无误
向下传递
- 在main.js里封装
$broadcast
Vue.prototype.$broadcast = function (eventName,value){
//获取当前组件下的所以孩子
const broadcast = (children) => {
children.forEach(child => {
child.$emit(eventName,value);
if(child.$children){
broadcast(child.$children)
}
})
}
broadcast(this.$children)
}
使用时只需要 在父级 this.$broadcast('say',1111) 只要子组件中有绑say的都给我说1111
在son1.vue中
<Grandson :money = money @say='say1'></Grandson>
methods:{
say1(val){
console.log("我很帅"+val)
}
}
<!--亲测控制台打印 我很帅1111-->
$attrs
属性的集合
$attrs
代表上级传过来的所有属性,当父组件给子组件需要穿过来很多值时,用props一个一个接收岂不是很累,使用$attrs
就可以一次性把父级传过来当所有值接收到,完美~- 直接用
$attrs
接收完数据,打开控制台,你会发现如下情况,属性全部挂载在了dom上,我们并不想这样 解决:在使用$attrs
的子组件上,添加inheritAttrs:false
,代码如下
<!--父组件-->
<son2 name="奔跑" address="北京"></son2>
<!--子组件-->
template>
<div>
<!-- $attrs代表上级传过来的所有属性 -->
儿子2: {{$attrs}}
</div>
</template>
<script>
export default {
inheritAttrs:false
}
</script>
<!--页面显示:儿子2: { "name": "奔跑", "address": "北京" }-->
- 如何子组件把获取到的数据再一次性传给孙子组件,只需要这么做:
<grandson2 v-bind="$attrs"></grandson2>
孙子组件接收时同样使用$attrs
即可获得所有的数据
$listeners
方法的集合
- 父级在子组件绑定的方法,在子组件里都可以用
$listeners
来调用这些方法 - 如果子组件不使用这些方法,孙子组件使用,则可以使用
v-on="$listeners"
来传递给孙子组件这些方法,孙子组件使用时,同样使用$listeners
<!--父组件-->
<son2 name="奔跑" address="北京" @look="console.log('look')"></son2>
<!--子组件-->
<grandson2 v-bind="$attrs" v-on="$listeners"></grandson2>
<!--孙子组件-->
<template>
<div>
孙子2:{{$attrs}}
<button @click="$listeners.look()">看</button>
</div>
</template>
<!--在孙子组件上点击按钮'看',则会在控制台打印'look'-->
provide
提供变量,inject
注册变量
- 问题:导航栏的地址发生改变但是页面却不刷新 (用vue-router路由到当前页面,页面是不进行刷新的),例如下面的搜索功能
- 1、
this.$router.go(0)
; - 2、
location.reload()
;
上面两种方法会出现闪屏的问题,用户体验不好
如何产品同意,我更喜欢window.open
一下 - 3、终极解决,哈哈哈
在App.vue,声明reload方法,控制router-view的显示或隐藏,从而控制页面的再次加载。(provide /inject )
【祖先组件(provide )向其所有子孙后代(inject )注入一个依赖】
- app.vue中
1.data中声明变量 2.绑定v-if 3.方法里写逻辑 4.向后代注入依赖
inject接受app传递的方法
$refs
获取实例
ref
被用来给元素或者子组件注册引用信息。引用信息将会注册在父组件的$refs
对象上。
如果在普通的Dom元素上使用,引用指向的就是DOM元素;
如果用在子组件上,引用就指向组件实例,父级就可使用子组件上的方法,通过$refs
;$refs
相对document.getElementById
的方法,会减少获取dom节点的消耗。$refs
只在组件渲染完成后才填充,并且它是非响应式当。它仅仅是一个直接操作子组件的应急方案---应当避免在模版或者计算属性中使用$refs
<!--父级-->
<son2 name="奔跑" address="北京" ref="son2"></son2>
mounted(){
this.$refs.son2.sleep()
},
<!--son2子组件-->
methods:{
sleep(){
console.log('我想睡觉')
}
}
跨组件通信 eventBus
eventBus
定义到了全局上,通过$on(eventName,function)
注册事件,通过$emit(eventName,参数)
获取使用事件
<!--在mian.js上注册bus-->
Vue.prototype.$bus = new Vue();
<!--在恰当的页面 注册事件-->
this.$bus.$on('有人找我了',function(val){
console.log('呵呵'+val)
})
<!--在恰当的页面 使用事件-->
this.$nextTick(()=>{
this.$bus.$emit('有人找我了','xxx')
})
<!--控制台打印-->
呵呵xxx
- this.$nextTick的用途
new Premise().then(()=>{
//等待所有代码都同步完以后再执行
})