在vue中组件是可复用的Vue实例,本质上是一个对象。而随着项目的不断增大,总是会有很多重复的模块,而把这些重复的模块提取出来封装成共用的组件。这无疑是大大的减少代码量与页面逻辑。当封装成组件时,如何给组件内变量赋值取值,这又成为了新的问题。这时候就需要了解几种组件之间的通信,更好的实现组件之间的信息的传递。
使用props属性
:适用于父子组件之间的通信 在父组件中调用子组件,在组件中以自定义属性的方式给子组件传值,子组件中使用props接收父组件的值;
<template name="父组件">
<div>
<!-- 使用自定义事件的方式传递给子组件监听事件 -->
<PageA :contA="contA" :textA="textA" @changeText="changeText" :objA="objA" />
<Button type="success" @click="changeText">父组件</Button>
</div>
</template>
<script>
import PageA from "@/components/pageA";
export default {
components: {
PageA
},
name: 'home',
data() {
return {
contA: '父组件contA',
textA: 5555,
objA: {name: "哈哈"}
}
},
methods: {
// 父组件改变textA的值
changeText(){
this.textA++;
}
}
}
</script>
<template name="子组件">
<div>
<h2>{{contA}}--{{textA}}</h2>
<Button type="success" @click="changeText">textA++</Button>
<div @click="objChange">{{objA.name}}</div>
</div>
</template>
<script>
export default {
name: 'pageA',
// 第一种props以数组的方式接收
// props: ['contA','textA'],
// 第二种props以对象形式接收可以给默认值
props: {
contA: {
type: String,
required: false, // 是否必须
default: '默认内容'
},
status: {
type: String,
required: true,
validator: function (value) {
return [
'error',
'success',
'fail',
].indexOf(value) !== -1
} // 验证传递过来的是否是数组其中一项
},
textA: {
type: Number,
required: true,
default: 77777
},
objA: {
type: Object,
default: {}
}
},
methods: {
changeText(){
// 第一种改变父组件值:通知父组件改变变量
this.$emit('changeText');
},
objChange(){
// 第二种改变父组件的值:此时可以改变父组件里面的值,因为父组件将objA的内存地址传递过来了,父子组件同用一个objA的内存地址,所以这里改变父组件的值。(请根据需求谨慎使用)
this.objA.name = "张三";
}
}
}
</script>
创建Vue实例作为事件中心
, e m i t / emit/ emit/on事件监听器: 适用兄弟之间传值,稍微复杂一些情况建议使用下面的vuex; 通过创建一个空的Vue实例作为事件中心,const EventVue = new Vue()
, 发送方利用Event. e m i t ( ′ 事 件 名 称 ′ , 传 递 的 值 ) , 接 收 方 利 用 E v e n t . emit('事件名称',传递的值),接收方利用Event. emit(′事件名称′,传递的值),接收方利用Event.on(‘事件名称’,事件处理函数); 注意:接收事件要放到created或之前触发监听;
<template name="兄弟A">
<div>
</div>
</template>
<script>
import Event from "@/utils/vm";
export default {
name: 'pageA',
data() {
return {
name: "a"
}
},
methods: {
send(){
Event.$emit('global',this.name);
}
}
}
</script>
<template name="兄弟B">
<div>
<h2>{{name}}</h2>
</div>
</template>
<script>
export default {
name: 'pageB',
data(){
return {
name: "888"
}
},
created(){
Event.$on('global',name=>{
this.name = name;
});
},
beforeDestroy(){
// 关闭事件监听器
Event.$off('global');
}
}
</script>
e m i t / emit/ emit/on事件监听器总结:
Event.$once('事件名',处理函数)
;vuex状态管理
:适用于大型项目中的数据存储,从而实现页面中的通信; 这里直接是使用vuex的方法,详细的vuex在之前笔记中,vue进阶——vuex状态管理。下面单纯的介绍一下用法:将多个页面需要的数据封装到一个 store 子仓库中,当需要的时候在仓库中拿去就好,利用 mutations 同步改变值;
// vuex仓库代码
<script>
// 这里是子仓库,记得要在主仓库中的modules中挂载
export default {
namespaced: true,
state() {
return {
number: 22
}
},
// 同步
mutations: {
SET_REDUCE(state) {
state.number--
},
SET_ADD(state) {
state.number++
}
}
}
</script>
<template>
<div>
<h2>vuex状态管理通信</h2>
<div>组件A</div>
<BaseStep class="base-step" :data="number" @reduce="SET_REDUCE" @add="SET_ADD" />
</div>
</template>
<script>
import { mapMutations, mapState } from "vuex";
export default {
// 利用计算器属性拿到store仓库数据
computed: {
...mapState({
number: state => state.Msg.number
})
},
methods: {
// 辅助方法映射出同步方法
...mapMutations("Msg", ["SET_REDUCE", "SET_ADD"]),
}
};
</script>
// 组件B与上面一样把仓库先导出进组件,可以使用组件同步方法与数据
ref、$parent和$children
: 适用于父子组件之间通信 ref表示一个对象,持有注册过 ref
attribute 的所有 DOM 元素和组件实例。 p a r e n t 与 parent与 parent与children都属于vue实例方法;获取对应的父组件或子组件的实例和方法。
// 获取组件的父组件的组件实例,同时可以使用父组件的变量与方法
console.log(this.$parent);
// 获取组件的所有子组件的实例,返回的子组件实例数组利用唯一值_uid区分,也可以使用子组件的变量与方法
console.log(this.$children);
// 假设在组件A中引用的组件B,给组件B的标签中ref名,就可以获取组件B的实例
console.log(this.$refs.pageB);
provide/inject
:适用层级嵌套比较深的组件之间通信 在vue2.2.0中新增的方法,祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。跨级组件之间建立了一种联系;
// 祖先组件中
data() {
return {
objProvide: {
id: 333
}
};
},
provide() {
return {
// 这里之所以绑定一个对象,主要是将内存地址传递给祖孙组件,这样数据就可以变成响应式
objProvide: this.objProvide
};
},
// 祖孙组件中接收值的方式可以效仿props
// inject: ["objProvide"],
inject: {
objProvide: {
type: Object,
default: {}
}
},
$attrs/$listeners
: 适用于多级嵌套的组件之间传递值 这对方法是vue2.4新增的,假设A组件传给子组件B自定义属性时,B组件没有使用props接收,便可以使用this.$attrs
拿到剩余属性值;在B组件中有C组件,C组件中可以使用this. a t t r s 拿 到 A 组 件 的 值 。 前 提 是 B 组 件 中 引 用 C 组 件 时 导 入 了 ‘ attrs拿到A组件的值。前提是B组件中引用C组件时导入了` attrs拿到A组件的值。前提是B组件中引用C组件时导入了‘attrs`对象;
// 以下组件的引入与注册省略了,请注意
// A组件
<template>
<B :obj1="obj1" @cFn="cFn" />
</template>
<script>
data(){
return {
obj1: {id: 2}
}
},
methods: {
cFn(){
console.log("C组件中调用方法")
}
}
</script>
// B组件
<template>
<C v-bind="$attrs" v-on="$listeners" />
</template>
<script>
mouthed(){
console.log(this.$attrs,"可以获取A组件中的obj1对象");
}
</script>
// C组件
<template>
<h1 @click="cFn">点击改变A组件的obj1</h1>
</template>
<script>
mouthed(){
console.log(this.$attrs,"可以获取A组件中的obj1对象");
},
methods: {
cFn(){
// 触发A组件的方法
this.$emit('cFn');
// 这里也可以直接改变A组件的值,因为传递过来的是一个对象
this.$attrs.obj1.id = 2222;
}
}
</script>
v-bind="$attrs"与v-on="$lienters"
就未被监听的数组传递给祖孙后代组件;inheritAttrs
属性,Boolean类型,默认值为true。如果是不想组件有继承特性,就可以设置为false,这样子代$attrs就拿不到值;
本地缓存进行组件之间的通信
:无法做到数据的响应式 有时候我们在很多地方都会需要常量,这些都是不经常变化的,例如登录信息等。每次调用接口获取就比较麻烦,这时就可以采用缓存进行存储,组件中需要时直接在缓存里面获取就好。注意:敏感信息最好不要存储到浏览器上,不安全。
// 原生app本地存储
plus.storage.setItem(key, value);
// uniapp本地存储
uni.setStroageSync(key,value);
// web端
localStorage.setItem(key,value);
// 小程序端
wx.setStroageSync(key,value);