1、父组件向子组件传值
父组件通过props 属性的方式向子组件传递数据。props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
<template>
<div>
<child :name-list="nameList"></child>
</div>
</template>
<script>
import comArticle from './Child.vue'
export default {
components: { Child },
data() {
return {
nameList: ['小明', '小强', '小张']
}
}
}
</script>
<template>
<div>
<span v-for="(name, index) in nameList" :key="index">姓名:{{name}}</span>
</div>
</template>
<script>
export default {
props: ['nameList']
props: {
nameList: {
type: Array,
default: () => {
return []
},
},
}
}
</script>
单向数据流 :所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定,父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。 如果在一个子组件内部改变 prop,Vue 会在浏览器的控制台中发出警告。
2、子组件向父组件传值
子组件向父组件传值可以通过$emit 实现,子组件通过$emit 绑定一个事件,当事件触发时会将参数传递给监听器回调,父组件通过v-on绑定这个事件监听器接收参数。
<template>
<div>
<child :@update-name="getName"></child>
</div>
</template>
<script>
import comArticle from './Child.vue'
export default {
components: { Child },
methods: {
getName(name) {
console.log('from child', name)
}
},
}
</script>
<template>
<div>
<span v-for="(name, index) in nameList" :key="index" @click="sendName(name)">姓名:{{name}}</span>
</div>
</template>
<script>
export default {
props: ['nameList']
props: {
nameList: {
type: Array,
default: () => {
return []
},
},
},
methods: {
sendName(name) {
this.$emit('update-name', name)
}
},
}
3、$parent / $children
$parent 可以访问到当前组件的父实例,$children 可以访问到当前实例的直接子组件。
<template>
<div>
<child :name-list="nameList"></child>
<button @click="changeChild">点击改变子组件值</button>
</div>
</template>
<script>
import comArticle from './Child.vue'
export default {
components: { Child },
data() {
return {
nameList: ['小明', '小强', '小张'],
age: 20
}
},
methods: {
changeChild() {
this.$children[0].title = '修改了子组件标题'
}
}
}
<template>
<div>
<h1>{{title}}</h1>
<span v-for="(name, index) in nameList" :key="index">姓名:{{name}}</span>
<h1>父组件的值: {{parentData}}</h1>
</div>
</template>
<script>
export default {
props: ['nameList']
props: {
nameList: {
type: Array,
default: () => {
return []
},
},
},
data() {
return {
title: '我是子组件的标题'
}
},
computed:{
parentData(){
return this.$parent.age;
}
}
}
$parent 获取的是父组件实例,如果没有则返回的是undefined $children 是获取当前实例的直接子组件,返回的格式是数组形式,如果没有子组件则返回一个空数组
4、$refs
$refs : 一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例。ref 如果绑定在普通DOM元素上,指向这个DOM元素;ref 如果绑定在组件上,指向该组件实例。
<template>
<div>
<child ref='childRef'></child>
<button @click="getChild">点击获取子组件实例</button>
</div>
</template>
<script>
import comArticle from './Child.vue'
export default {
components: { Child },
methods: {
getChild() {
const childRef = this.$refs.childRef
console.log(childRef.title);
}
}
}
<template>
<div>
<h1>{{title}}</h1>
</div>
</template>
<script>
export default {
data() {
return {
title: '我是子组件的标题'
}
},
}
5、provide / inject (2.2.0 新增)
- provide:
Object | () => Object - inject:
Array<string> | { [key: string]: string | Symbol | Object }
provide / inject 需要一起使用,父组件通过privide 提供变量,子孙组件通过inject 注入这个变量依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 缺点: 官网不建议在应用中直接使用该办法,理由很直接:他怕你"管不好"
设计项目,通常追求有清晰的数据流向和合理的组件层级关系便于调试和维护,然而这对选项支持任意层级都能访问,导致数据追踪比较困难。不知道那一层级声明了provide又或是哪些层级使用了inject。造成比较大的维护成本。因此,除组件库或高阶插件外,Vue建议用Vuex解决或其他办法处理
var Provider = {
provide: {
foo: 'bar'
},
provide() {
return {
foo: this
}
}
}
var Child = {
inject: ['foo'],
created () {
console.log(this.foo)
}
}
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。 provide就相当于加强版父组件prop,可以跨越中间组件,inject就相当于加强版子组件的props 对于一些简单的数据可以使用provide / inject
6、$attrs / $listeners
使用 v-bind=”$attrs”, 将父组件中不被认为 props 特性绑定的属性传入子组件中,通常配合 interitAttrs 选项一起使用。之所以要提到这两个属性,是因为两者的出现使得组件之间跨组件的通信在不依赖 vuex 和 事件总线 的情况下变得简洁,业务清晰。
inheritAttrs: 默认值 true,继承所有的父组件属性(除 props 的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置 inheritAttrs: false ,但是 class 属性会继承。 (简单的说,inheritAttrs:true 继承除 props 之外的所有属性;inheritAttrs:false 只继承 class style 属性
$attrs $attrs–继承所有的父组件属性(除了 prop 传递的属性、class 和 style ),一般用在子组件的子元素上2.4.0 新增 类型:{ [key: string]: string } 详细: 包含了父作用域中 不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v-bind=”attrs”, 传入内部组件——在创建高级别的组件时非常有用。
$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件——在创建更高层次的组件时非常有用。
<template>
<div class="main">
<child
child="父组件"
placeholder="占位符"
@parentMethods="onMethods1($event)"
></child>
</div>
</template>
<script>
import Child from "./child.vue";
export default {
name: "Login",
components: { Child },
methods: {
onMethods1(e) {
console.log(e);
}
},
};
</script>
<template>
<div>
<h1>child</h1>
<p style="color: red">$attrs: {{ $attrs }}</p>
<son
class="child"
:son="childname"
v-bind="$attrs"
@childMethods="onChildMethods($event)"
v-on="$listeners"
></son>
</div>
</template>
<script>
import Son from "./gradson.vue";
export default {
name: "Child",
components: { Son },
inheritAttrs: true,
props: {
child: {
type: String,
default: () => "",
},
},
data() {
return {
childname: "child",
};
},
methods: {
onChildMethods(e) {
console.log(e);
},
},
};
</script>
<template>
<div>
<h1>son</h1>
<p style="color: red;">$attrs: {{ $attrs }}</p>
<p>
<button @click="getMethods()">点击触发父作用域中的事件监听器</button>
</p>
</div>
</template>
<script>
export default {
name: "Son",
inheritAttrs: false,
props: {
son: {
type: String,
default: () => "",
},
},
methods: {
getMethods() {
console.log(this.$attrs);
this.$emit('parentMethods', 'son 组件触发parent methods---parentMethods')
this.$emit('childMethods', 'son 组件触发child methods---childMethods')
}
}
};
</script>
<style></style>
7. eventBus(事件总线)
兄弟组件通讯可以借助VueX来实现,但对于一些简单结构的项目不需要引入VueX造成项目臃肿,或者通过子传父再父传子实现数据的传递。 创建event-bus.js实质上就是导出了一个空的Vue实例,这个实例上挂载了vue的实例方法和事件,外部可以使用$emit/$on 发布和订阅事件。 $emit 触发当前实例上的事件。附加参数都会传给监听器回调。 $on 监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。
import vue from 'Vue'
const eventBus = new Vue()
export default eventBus
使用
import { eventBus } from 'event-bus.js'
eventBus.$emit('methods', params)
import { eventBus } from 'event-bus.js'
eventBus.$on('methods', (params) => {
...
})
或者直接将 eventBus 挂载到vue原型上,注册一个全局的事件总线
Vue.prototype.$EventBus = new Vue()
移除监听事件
beforeDestroy () {
eventBus.$off('methodsName')
eventBus.$off()
}
|