1. props / $emit
- props 父组件传值给子组件
- $emit 子组件传值给父组件
props:
// 父组件
<template>
<div>
<Child v-bind:list="users"></Child>
</div>
</template>
<script>
import Child from "./components/Child" //子组件
export default {
name: 'App',
components:{Child},
data(){
return{
users:["Eric","Andy","Sai"] }
}
}
}
</script>
// 子组件
<template>
<ul>
<li v-for="item in list">{{ item }}</li>
</ul>
</template>
<script>
export default {
name: 'child',
props:{
list:{ //这个就是父组件中子标签自定义名字
type:Array, //对传递过来的值进行校验
required:true //必添
}
}
}
</script>
$emit:
// 子组件
<template>
<div>
<h1 @click="changeTitle">{{ title }}</h1> //绑定一个点击事件
</div>
</template>
<script>
export default {
name: 'header',
data() {
return {
title:"子组件title"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged", "子向父组件传值")
}
}
}
</script>
// 父组件
<template>
<div>
<header v-on:titleChanged="updateTitle"></header>
<h2>{{ title }}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
components:{ Header },
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
}
}
</script>
2. $attrs / $listeners
- $attrs:包含了父作用域中不被 Prop 所识别的特性绑定(class 和 style 除外),当一个组件没有申明任何 prop 时,这里会包含所有父作用域的绑定(class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件
- $listeners:包含了父作用域中的 v-on 事件监听器(不包含 .native 修饰器的),可以通过 v-on="$listeners" 传入内部组件
$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性, $listeners里存放的是父组件中绑定的非原生事件。
// childCom1
<template>
<div class="border">
<h2>childCom1</h2>
<child-com2 title="前端工匠" :aoo="aoo" :boo="boo" :coo="coo"
:style="{background: '#f2f2f2'}" class="box"
@msgChanged="updateMsg"></child-com2>
<p>{{msg}}</p>
</div>
</template>
<script>
import ChildCom2 from '@/views/childCom2.vue'
export default {
name: 'childCom1',
components: { ChildCom2 },
data () {
return {
aoo: 'Html',
boo: 'CSS',
coo: 'Javascript',
msg: ''
}
},
methods: {
updateMsg (msg) {
this.msg = msg
}
}
}
</script>
// childCom2
<template>
<div class="border">
<h2>childCom2</h2>
<p>childCom2的$attrs: {{ $attrs }}</p>
<child-com3 v-bind="$attrs" v-on="$listeners"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import('./childCom3.vue')
export default {
name: 'childCom2',
components: {
childCom3
},
inheritAttrs: false,
props: {
aoo: String
}
}
</script>
注意: 这里是 v-bind= 和 v-on=。 这里v-on是等号。
// childCom3
<template>
<div class="border">
<h2>childCom3</h2>
<p @click="changeMsg">childCom3的$attrs: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
name: 'childCom3',
inheritAttrs: true,
props: {
coo: String,
title: String
},
methods: {
changeMsg () {
this.$emit('msgChanged', '我是childCom3的数据')
}
}
}
</script>
childCom2 中,$attrs 里没有 style 和 class 的内容。因为 props 中申明了 aoo ,所以也没有 aoo 。
关于 inheriAttrs 的作用
childCom2 中 inheriAttrs:false,childCom3 中 inheriAttrs:true。渲染 DOM 如下:
可以看到为 true 时,childCom3 组件中的根节点上绑定了 $attrs 的属性
3. provide / inject
- provide 在祖先中提供变量
- inject 在后代中注入变量
provide / inject 只能用于父传子,反向不能传递。
// grandpa.vue
export default {
name: 'grandpa',
// provide: {
// name: '前端工匠'
// },
provide () {
return {
name: '前端工匠',
num: this.count
}
},
data () {
return {
count: 3
}
}
}
// grandson.vue
export default {
name: 'grandson',
inject: ['name', 'num'],
created () {
console.log(this.name)
console.log(this.num)
}
}
此时传递的数据,不是响应式的。grandpa 中修改 count 的值,grandson 中的 num 并不会改变。
实现响应式的两种方法:
- provide祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在子孙组件中直接获取或者修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西比如props, methods。
- Vue.observable 优化响应式 provide
第一种方法示例如下:
// grandpa.vue
export default {
name: 'grandpa',
// provide () {
// return {
// name: '前端工匠',
// num: this.count
// }
// },
provide () {
return {
grandpaInfo: this
}
},
data () {
return {
name: '前端工匠',
count: 3
}
},
methods: {
changeNum () {
this.count++
}
}
}
// grandson.vue
export default {
name: 'grandson',
// inject: ['name', 'num'],
inject: ['grandpaInfo'],
created () {
// console.log(this.name)
// console.log(this.num)
console.log(this.grandpaInfo.name)
console.log(this.grandpaInfo.count)
}
}
此时 grandpa 中的count 改变,grandson 中的 count 也会跟着变化
inject 也可以对祖先传过来的实例重新命名:
export default {
name: 'grandson',
// inject: ['grandpaInfo'],
inject: {
grandpaInfoObj: {
from: 'grandpaInfo',
default () {
return {}
}
}
},
created () {
// console.log(this.grandpaInfo.name)
// console.log(this.grandpaInfo.count)
console.log(this.grandpaInfoObj.name)
console.log(this.grandpaInfoObj.count)
}
}
第二种方法示例如下:
// grandpa.vue
import Vue from 'vue'
export default {
name: 'grandpa',
provide () {
this.grandpaInfo = Vue.observable({
name: '前端工匠',
count: 3
})
return {
grandpaInfo: this.grandpaInfo
}
}
}
// grandson.vue
export default {
name: 'grandson',
inject: ['grandpaInfo'],
created () {
console.log(this.grandpaInfo.name)
console.log(this.grandpaInfo.count)
}
}
此时 grandpa 中修改 this.grandpaInfo 中的值,grandson 中的值跟着改变。
4. $parent / $children
- $parent 访问父实例
- $children 访问子实例
// parent.vue
<script>
import Child from './child.vue'
export default {
name: 'parent',
components: { Child },
data () {
return {
title: '我是父组件',
childTitle: ''
}
},
methods: {
sayHello () {
console.log('hello')
},
getChild () {
this.childTitle = this.$children[0].title
this.$children[0].sayBye()
}
}
}
</script>
// child.vue
<script>
export default {
name: 'child',
data () {
return {
title: '我是子组件',
parentTitle: ''
}
},
methods: {
sayBye () {
console.log('bye')
},
getParent () {
this.parentTitle = this.$parent.title
this.$parent.sayHello()
}
}
}
</script>
使用 $parent 和 $children 时,需要注意以下几点:
- 使用 $parent 时,注意一些UI框架的组件嵌套。比如页面中如果 child 组件的外层有 el-row 包裹,那 child 的$parent 是 el-row 实例,需继续向上查找,才是 parent 组件实例。
- 使用 $children 时,注意 children 的下标,是页面最终呈现时的下标。比如,如果 child 组件上有一个动态组件,只有某种情况下才显示,平时不显示。那么当动态组件显示时,child 的下标就会发生变化。
5.事件总线 eventbus ,$emit / $on
创建 bus 文件(新建js文件)
import Vue from 'vue'
export defult new Vue
使用:使用 bus 的组件,必须引入 bus 文件
// 组件 aa ,发送数据
<template>
<div>
<h3>aa组件</h3>
<button @click="sendMsg">将数据发送给bb组件</button>
</div>
</template>
<script>
import bus from './bus'
export default {
methods: {
sendMsg(){
bus.$emit('sendTitle','传递的值')
}
}
}
</script>
// 组件 bb,接收数据
<template>
<div>
接收aa传递过来的值:{{msg}}
</div>
</template>
<script>
import bus from './bus'
export default {
data(){
return {
mag: ''
}
}
mounted(){
bus.$on('sendTitle',(val)=>{
this.mag = val
})
}
}
</script>
6. $root
$root 获取当前 vue 组件的根组件实例。
当兄弟组件之间传值,或者是跨层级传值时,可以将数据挂载到根实例上,然后其他组件从根实例上获取。
// child1.vue
methods: {
setData () {
this.$root.myData = '使用$root传递数据'
}
}
// child2.vue
methods: {
getData () {
console.log(this.$root.myData)
}
}
7. ref
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
给子组件添加 ref ,在父组件中可以通过 this.$refs.['设置的名称'] 访问子组件实例。
// parent.vue
<template>
<div class="border">
<h2>parent</h2>
<child2 ref="child2"></child2>
<div @click="getChild">获取子节点数据</div>
</div>
</template>
<script>
import Child2 from './child2.vue'
export default {
name: 'parent',
components: { Child2 },
methods: {
getChild () {
console.log(this.$refs.child2.type)
}
}
}
</script>
// child2.vue
<script>
export default {
name: 'child2',
data () {
return {
type: 'css'
}
}
}
</script>
需要注意的是,如果组件有循环,那 this.$refs.child2 获取到的是 Array:
<child2 ref="child2"
v-for="(item, index) in list"
:key="index"></child2>
export default {
name: 'parent',
data () {
return {
list: ['a', 'b', 'c']
}
}
}
此时 this.$refs.child2 的值为
8. vuex
组件间传值,也可以使用 vuex。利用 vuex 实现数据共享。