传值就是为了联动,能够及时准确传值获取值才是王道。
props是父传子,$emit是子传父。
父组件的中的子组件标签上我们加入的options,headerData,tableData三个变量是准备要传给子组件的。
props: {
tableData: {
type: Array,
default: () => {
return []
}
},
headerData: {
type: Array,
default: () => {
return []
}
},
options: {
type: Object,
default: () => {
return {
multiSelect: true, // boolean 是否多选
singleSelect: false, // boolean 是否单个checkbox
isindex: false, // boolean 是否展示序列号
stripe: true, // boolean 斑马纹
border: true, // boolean 纵向边框
size: 'medium', // String medium / small / mini table尺寸
fit: true, // 自动撑开
pagination: true, // 是否有分页
numPage: false
}
}
},
}
属性/方法 | 作用 |
type | 传值的类型 |
default | 默认值;如果没有传,就使用 |
requied | 值为true/false,代表是否父组件是否必须传 |
validator(val){} | 可以判断父组件传过来的值是否有效 |
props:{
name:{
type:String,
default:'默认为王三',
required:true,
validator(val){
return val>0&&val<10
}
}
}
that.$emit('usbKeyForm', that.usbKeyForm)
methods: {
usbKeyForm(val) {
this.SerialNumber = val.SerialNumber
},
}
思考:为什么两种方法不一样呢?
首先要明白传值的前提,为什么要传值,因为作用域。其实在Vue.js中每个组件都有自己的作用域,而且每个组件定义了一个props属性可以去接收父组件传过来的值,而且定义了type类型就是为了确定父级传过来的值的类型就是我们需要的,当然也可以修饰数据。如果加入required属性,值为true的时候,要求父组件必须要传的。default是设置默认值的,如果父组件没有传我们就使用默认值。所以我们经常看到使用props时候经常使用type+default两个属性搭配使用。
$emits传值原理是通过事件系统进行传值的,因为作用域是上下关系,你要是像下上关系,就得需要闭包传值。
组件关系A组件---》B组件----》C组件;大家都知道props和$emit是最常用的传值方法,但是仅限A到B或者B到A,如果A到C或者C到A呢?就需要用到$attrs和$listeners了
$attrs可以继承父组件里边的属性数据,并且携带下去。
通俗地来说就是如果从父组件传过来的值,没有在子组件中被接收,那么这些值就会被存在$attrs对象中。
儿子组件:{{$attrs.name}}
inheritAttrs: false 的含义是不希望本组件的根元素继承父组件的attribute,同时父组件传过来的属性(没有被子组件的props接收的属性),也不会显示在子组件的dom元素上,但是在组件里可以通过其$attrs可以获取到没有使用的注册属性。
inheritAttrs: false 是不会影响 style 和 class 的绑定
inheritAttrs: true
inheritAttrs: false 不想继承所有父组件的内容,同时也不在组件根元素dom上显示属性
包含了父作用域中的(不包含.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件–在创建更高层次的组件时非常有用。
通俗地来说就是可以从孙子组件发送事件到父子组件中。
//Father.vue
父亲
//Son.vue
儿子
//GrandSon.vue
孙子
普通的emit只能从子组件传到父组件,通过$listeners,我们可以实现从孙子组件把事件传到父子组件,不需要通过emit逐层传递。
$attrs和$listeners主要用来父孙组件之间传值,有的同学说也可以用provide和inject来实现父孙组件之间传值,但需要注意的是provide和inject只能从父组件向孙组件传值,不能从孙组件向父组件传值,这种情况就需要使用$attrs和$listeners来实现了。
上面两种方式处理的都是清楚父子组件之间的关系情况下传输数据,而如果不清楚两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus.$emit触发事件,bus.$on监听触发的事件。
使用方法:可以声明一个全局变量来使用事件中心,但如果在使用 webpack 之类的模块系统,这显然不合适。
每次使用都手动 import 进来也很不方便,所以本文使用 vue-bus 插件。
vue-bus npm地址
vue-bus github地址
npm install vue-bus --save
如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装 vue-bus:
// main.js
import Vue from 'vue';
import VueBus from 'vue-bus';
Vue.use(VueBus);
假设有两个Vue组件需要通信 ,在 A 组件的按钮上面绑定了点击事件发送一则消息,想通知 B 组件。
// A 组件
// B 组件
{{Bmsg}}
eventBus 适合小项目、数据被更少组件使用的项目,对于中大型项目数据在很多组件之间使用的情况 eventBus 就不太适用了。eventBus 其实就是一个发布订阅模式,利用 Vue 的自定义事件机制,在触发的地方通过 $emit 向外发布一个事件,在需要监听的页面,通过 $on 监听事件。
简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
需要注意的是这里不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据。
成对出现:provide和inject是成对出现的
作用:用于父组件向子孙组件传递数据
使用方法:provide在父组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。
使用场景:由于vue有$parent属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject可以轻松实现跨级访问父组件的数据
//父组件
provide: {
user: 'John Doe'
}
--------------------
//子组件
inject: ['user']
//使用
console.log(this.user)
缺点:如果我们尝试在此处提供一些组件实例 property,则这将不起作用,
provide: {
todoLength: this.todos.length // 将会导致错误 'Cannot read property 'length' of undefined`
},
provide() {
return {
todoLength: this.todos.length
}
},
缺点:数据不会响应变化
在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。
provide 函数允许你通过两个参数定义 property:
property 的 name ( 类型)
property 的 value
//父组件
import { provide } from 'vue'
export default {
setup() {
provide('location', 'North Pole')//location 键(标识符)-- North Pole //值
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
// 子组件
父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input',val)自动修改v-model绑定的值
Vue.component('child',{
props:{
value:String, //v-model会自动传递一个字段为value的prop属性 },
data(){ return {
mymessage:this.value
}
},
methods:{
changeValue(){ this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值 }
},
template:`
})
Vue.component('parent',{
template:`
this is parent compoent!
{{message}}
`,
data(){ return {
message:'hello'
}
}
}) var app=new Vue({
el:'#app',
template:`
`
})
在组件内部可以直接通过子组件$parent对父组件进行操作,父组件通过$children对子组件进行操作.
Vue.component('child',{
props:{
value:String, //v-model会自动传递一个字段为value的prop属性 },
data(){ return {
mymessage:this.value
}
},
methods:{
changeValue(){ this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值 }
},
template:`
})
Vue.component('parent',{
template:`
this is parent compoent!
`,
methods:{
changeChildValue(){ this.$children[0].mymessage = 'hello';
}
},
data(){ return {
message:'hello'
}
}
}) var app=new Vue({
el:'#app',
template:`
`
})
如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。
通过
window.localStorage.getItem(key)
获取数据通过
window.localStorage.setItem(key,value)
存储数据注意用JSON.parse() / JSON.stringify() 做数据格式转换
localStorage / sessionStorage可以结合vuex,实现数据的持久保存,同时使用vuex解决数据和状态混乱问题。
传值方法 | 特点及使用场景 |
props/$emit | 确定父子关系的父子组件 |
$attrs/$listeners | 确定关系的多层组件之间 |
中央事件总线 | 不确定关系的组件,但是适合小项目,因为这个是借助于事件 |
provide/inject | 通过provide/inject可以轻松实现跨级访问父组件的数据,比较消耗性能 |
v-model | 父传子使用,但是有了props为什么我要用这个呢,所以用处不大 |
$parents/$children | 在组件内部可以直接通过子组件$parent对父组件进行操作,父组件通过$children对子组件进行操作. |
vuex | 处理公共数据,更组件无关系,专门的数据状态管理。 |
浏览器缓存 | 为了长时间留存数据使用的,用的也比较多。 |
$ref/$refs | 获取实Dom上的数据,从上至下 |
smallSkill:我们传值时候为了不影响的父值的变化,我们可以用变量接收的方法去解决这个问题(这个面试常常问到)
本文就到此结束了,希望大家共同努力,早日拿下 Vue。
如果文中有不对的地方,或是大家有不同的见解,欢迎指出 。
如果大家觉得所有收获,欢迎一键三连。