vue组件传值
最近准备面试跳槽。说到前端面试,有一些知识尤为重要,例如本篇文章要讲到的vue组件传值,几乎每家公司都会问到,所以在这里结合实战来总结一下。相信vue组件化对于前端开发者来说是家常便饭,所以组件之间的传值就变得尤为重要。本文章分为介绍、父子组件传值、兄弟组件传值、多层组件传值实操概括一下所有内容。
vue组件化
什么是组件化?
就拿开发一个页面来说,如果你把一个页面的所有逻辑都放到了一起,那样代码看起来就会非常复杂,处理起来会非常麻烦,而且重复性高。假如你是开发后台管理系统的,那么将会有70%的功能是重复的,相似功能的代码重复的写,这样就会造成代码冗余。所以把一些重复使用的功能分出来单独模块管理,这样在需要的地方引进便可以了。因此组件化对于前端开发尤为重要,可以把页面看成是很多组件结合而成。
如下图:
vue组件通讯
上面讲到vue组件化思想,现在开始进入正题,讲解一下vue组件之间的通讯方式以及方法。如果一个组件引入另一个组件,那么这个组件就是主组件,被引进的组件就是子组件。
父组件向子组件传值
父组件first.vue
子组件childA
。
父组件通过v-bind(
:
)向子组件传值,子组件通过props
来接收。
// 父组件
<!-- 赋值给子组件A -->
<childA :mainstring="main"></childA>
// 子组件
props:{
mainstring: {
type: String, //接收类型
requires: true //固定写法
}
},
//以下为其他几种写法
--------------------------------------------------------------------------------------
props: { mainstring: String }
props: { mainstring: [String, Number] } //多个可能类型
props: {
childMsg: {
type: Array,
default: () => []
}
} // default指定默认值
props接收主组件的type类型可以分为:
- 普通类型:字符串(
String
)、数字(Number
)、布尔值(Boolean
)、空(Null
)
因为vue是基于单向数据流的原理,父级 prop 的更新会向下流动到子组件中,但是反过来则不行,子组件中改变了值不会流向父组件并造成报错,因此可以赋值给childA,再进行更改就不会报错了。
props:{
mainstring: {
type: String,
requires: true //固定写法
}
},
data() {
return {
childA:this.mainstring
}
},
methods: {
// 重新赋值
change(){
this.childA = '子组件内部重新赋值'
}
}
另外,子组件接收的mainstring值不会随着父组件的改变而改变,所以如果需要子组件的mainstring值随父组件改变,则需要建立一个watch监听来完成这一需求。
// 监听父组件mainstring值改变,进入改变childA值
watch: {
mainstring(newval) {
this.childA = newval
}
}
- 引用类型:数组(
Array
)、对象(Object
)
如果是引用类型,当在子组件中修改后,父组件的也会修改,因其数据是公用的,其他同样引用了该值的子组件也会跟着被修改。可以理解成父组件传递给子组件的值,就相当于复制了一个副本,这个副本的指针还是指向父组件中的那个,即共享同一个引用。所以除非有特殊需要,否则不要轻易修改。
子组件向父组件传值
- 子组件通过v-on(
@
)绑定一个方法,在方法中通过$emit
传值
假设子组件有一个按钮,当我们点击这个按钮,子组件通过
$emit
向父组件传一个值,父组件拿到这个值渲染到页面上。
// 子组件
// 点击把 child 传给父组件
<a-button type="primary" @click="change">传值</a-button>
export default {
data() {
return {
childA:'我是子组件的值'
}
},
methods: {
// 触发change方法
change() {
this.$emit('changechild',this.childA) //传值操作,changechild为父组件绑定的方法
}
}
}
// 父组件
<span>主组件:{{ main }}</span>
<childA @changechild="changestring"></childA>
export default {
components:{ childA },
data() {
return {
main:"",
}
},
methods: {
// 接收子组件的传值
changestring(name) {
this.main = name
}
}
}
- 通过callback函数
父组件定义一个
callback
函数,通过prop传给子组件,子组件触发callback
方法。
// 父组件
<span>主组件:{{ main }}</span>
<childA :callback="change"></childA>
import childA from './childA.vue'
export default {
components:{ childA },
data() {
return {
main:""
}
},
methods: {
// 接收子组件传过来的值
change(name) {
this.main = name
},
}
}
// 子组件
<a-button type="primary" @click="callback(childA)">传值</a-button>
export default {
props: {
callback: Function
},
data() {
return {
childA:'我是子组件的值'
}
}
}
- 通过
$refs
访问组件实例
这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。
// 父组件
<a-button type="primary" @click="change">点击触发子组件方法</a-button>
<childA ref="callchild"></childA>
import childA from './childA.vue'
export default {
components:{ childA },
methods: {
// 点击触发子组件方法 -- ref方法
change() {
// 通过ref方法
this.$refs.callchild.callprint()
},
},
}
// 子组件
callprint() {
console.log('Hello World')
}
注:这种方式的组件通信不能跨级。
- $attrs / $listeners 后面会出一篇文章详细讲。
兄弟组件传值
- 通过props和$emit两者结合
现在有三个组件,分别为父组件和它的两个子组件,这两个子组件都在父组件下,没有上下级关系,因此称为兄弟组件。改变其中一个值,另外一个兄弟组件的某个值要被改变。可以先改变组件A值之后去触发
$emit
返回父组件,父组件再去改变传到组件B的值,组件B只需要用watch判断,实时渲染便可。
// 父组件
<!-- 子组件A -->
<childA :mainstring="main" @changechild="changestring"></childA>
<!-- 子组件B -->
<childB :mainstring="main"></childB>
import childA from './childA.vue'
import childB from './childB.vue'
export default {
components:{ childA,childB },
data() {
return {
main:"我是父组件的值",
childA:"",
childB:""
}
},
methods: {
// 改变传给子组件的值
changestring(name) {
this.main = name
}
},
}
// childA组件
<span>子组件A:{{ childA }}</span>
<a-button type="primary" @click="change">传值</a-button>
export default {
// 接收父组件的值
props: {
mainstring: {
type:String,
requires:true
}
},
data() {
return {
childA:this.mainstring
}
},
methods: {
// 传出新的值给父组件
change() {
this.childA = '我是childA的值'
this.$emit('changechild',this.childA)
}
}
}
<span>子组件B:{{ childB }}</span>
export default {
// 接收父组件的值
props: {
mainstring: {
type:String,
requires: true
}
},
data() {
return {
childB:this.mainstring
}
},
// 监听父传子值改变赋值
watch:{
mainstring(newVal) {
this.childB = newVal
}
}
}
- 新建一个vue实例,连接着两个组件。
创建一个空的 vue 实例
EventBus.js
文件来作为事件中心或者说是中转站,用来传递和接收事件,然后在需要的地方import引进即可,也可直接挂载在main
上面。用法:假设兄弟A和B,A通过$emit触发事件发送至中间站EventBus.js
,EventBus.js
接收之后。B通过 $on监听中间件方法回调,获取数据。此种方法适用于任何组件间的通信。包括父子、兄弟、跨级。
// EventBus.js
import Vue from 'vue'
export default new Vue()
// childA.vue
<a-button type="primary" @click="change">修改传值</a-button>
import EventBus from './EventBus.js'
change() {
this.name = 'Lity'
EventBus.$emit('getchildAchange',this.name) //发送到中间站
}
// childB.vue
import EventBus from './EventBus.js'
created() {
EventBus.$on('getchildAchange', (name) => {
this.childB = name //name就是childA传到中间站的值
})
}
- 通过 vuex 来处理,后面会出一篇文章详细讲。
上面通过创建一个vue实例,来实现数据的中间站处理可针对于一些简单的项目,对于更大型的、更复杂的项目,推荐使用 vue 提供的状态管理模式 Vuex 来进行处理。
总结
- 父传子:props直接传值;新建vue实例EventBus.js;
- 子传父:$emit;callback函数; $refs访问组件实例; $attrs / $listeners;
- 兄弟传值:props和$emit结合;新建vue实例EventBus.js;使用vuex;