组件间的通信方式
1、props
父组件向子组件传数据,子组件接收到数据之后,不能直接修改父组件的数据。否则会报错,因为当父组件重新渲染时,数据会被覆盖。
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,(仅仅只能接收,props是单向绑定的,即只能父组件向子组件传递,不能反向)若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
- 父组件
<template>
<div>
<h1>我是父组件</h1>
<br>
<p>--------------------------</p>
<br>
<child :msg='message'></child>
</div>
</template>
<script>
import child from '../zjlianxi/child.vue';
export default {
components: { child },
data() {
return {
message:'我是父组件的msg'
};
},
};
</script>
- 子组件
//child.vue
<template>
<div>
<h1>我是子组件</h1>
<br/>
<h1>{{msg}}</h1>
</div>
</template>
<script>
export default {
props:['msg'],
mounted() {
console.log(this.msg)
},
};
</script>
扩展—props
props:{
title:{
type:String,
default:"默认标题"
},
time:{
type:Number,
default:Date.now()
},
list:{ -------------------注意数组和对象默认值返回格式
type:Array,
default(){
return [1,2,3]
}
},
user:{
type:Object,
default(){
return {name:"匿名",gender:"保密"}
}
}
},
2、$emit / v-on
子组件通过派发事件的方式给父组件数据,或者触发父组件更新等操作
- 父组件
//father.vue
<template>
<div>
<h1>我是父组件</h1>
<br>
<h1>{{msg}}</h1>
<p>--------------------------</p>
<br>
<child @sendMsg='getMsg'></child>
</div>
</template>
<script>
import child from '../zjlianxi/child.vue';
export default {
components: { child },
data() {
return {
msg:""
};
},
methods: {
getMsg(msg){
this.msg = msg
console.log(msg) //接收子组件传过来的数据
},
},
};
</script>
- 子组件
<template>
<div>
<h1>我是子组件</h1>
<br/>
<el-button @click='changeMsg'>添加</el-button>
</div>
</template>
<script>
export default {
data() {
return {
msg:'这是给父组件传的信息'
};
},
methods: {
changeMsg(){
this.$emit('sendMsg',this.msg)
}
},
};
</script>
总结:子组件想要将字符串’'data"传到父组件中。首先需要创建一个事件,在这个事件函数changeMsg中,使用this. e m i t ( ) ,即: t h i s . emit(),即:this. emit(),即:this.emit(“sendMsg”, “data”)。
在父组件中,监听自定义事件sendMsg,当自定义事件触发时,执行新函数getMsg,即:@sendMsg=‘getMsg’
在执行的新函数getMsg中,就可以获取到从子组件传递过来的值,并且自由使用。
3、ref / $refs
ref属性:
1.被用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识:<h1 ref=" xxx">.....</h1>或<School ref=" xxx" ></School>
获取: this.$refs.xxx
作用:父组件可以通过this.$refs.xxx的方式去获得子组件的值和调用子组件方法 (xxx是属性ref的值)
代码思路:点击父组件的点击事件,输出子组件的number值20,然后调用子组件的changeAge方法,将number值修改为50,最后输出修改后的number值
- 父组件
<template>
<div>
<h1>我是父组件</h1>
<br />
<el-button @click="sendMethod">修改子组件number</el-button>
<p>--------------------------</p>
<br />
<child ref="childRef"></child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: { child },
methods: {
sendMethod() {
console.log("点击前", this.$refs.childRef.number); //点击前 20
this.$refs.childRef.changeNumber();
console.log("点击后", this.$refs.childRef.number); //点击后 50
},
},
};
</script>
- 子组件
//child.vue
<template>
<div>
<h1>我是子组件</h1>
<br />
<h1>{{ number }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
number: 20,
};
},
methods: {
changeNumber() {
this.number = 50;
},
},
};
</script>
4、$parent + $children
(1)$children:
官方介绍:当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。
即$children是组件自带的属性,它可以获取到当前组件的子组件,并以**数组**的形式返回。
(2)$parent:
官方介绍:指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。
如果组件没有父组件,他的 p a r e n t 为 u n d e f i n e d , A p p 组件 ( 根组件 ) 的 parent为undefined,App组件(根组件)的 parent为undefined,App组件(根组件)的parent不是undefined,也不是App本身。
注意:节制地使用 $parent 和 $children - 它们的主要目的是作为访问组件的应急方法。弊端是:无法在跨级或兄弟间通信.
$parent 获取父节点中所有数据和方法
- 父组件
<template>
<div>
<h1>我是父组件</h1>
<br />
<p>--------------------------</p>
<br />
<child @sendMethod='sendMethod' :number="number"></child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: { child },
data() {
return {
message: "我是父组件的msg",
number: 20,
};
},
methods: {
sendMethod() {
this.number = 50 ----给子组件的
},
},
};
</script>
- 子组件
<template>
<div>
<h1>我是子组件</h1>
<br />
<h1>{{ parentMessage }}</h1>
<h1>{{number}}</h1>
</div>
</template>
<script>
export default {
props:['number'],
mounted() {
this.$parent.sendMethod() //获取父组件的方法 未调用时number为20,调用时number为50
},
computed: {
parentMessage() {
return this.$parent.message; //获取父组件中的属性
},
},
};
</script>
$children 获取子组件中所有数据和方法
- 父组件
<template>
<div>
<h1>我是父组件</h1>
<br />
<h1>{{name}}</h1>
<p>--------------------------</p>
<br />
<child></child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: { child },
data() {
return {
name:''
};
},
mounted() {
// this.$children[0].sendMethod() //调用子组件的方法 未调用时name:小李,调用时name:小王
this.name =this.$children[0].name //获取子组件的属性值 小李
},
};
</script>
- 子组件
<template>
<div>
<h1>我是子组件</h1>
<br />
</div>
</template>
<script>
export default {
data() {
return {
name:'小李'
};
},
methods: {
sendMethod(){
this.name = '小王'
},
},
};
</script>
5、provide + inject
provide/inject可以轻松实现跨级访问父组件的数据
provide / inject
是依赖注入,在一些插件或组件库里被常用
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。(如果你熟悉 React,这与 React 的上下文特性很相似。)
provide:可以让我们指定想要提供给后代组件的数据或方法
inject:在任何后代组件中接收想要添加在这个组件上的数据或方法,不管组件嵌套多深都可以直接拿来用
要注意的是 provide 和 inject 传递的数据不是响应式的,也就是说用 inject 接收来数据后,provide 里的数据改变了,后代组件中的数据不会改变,除非传入的就是一个可监听的对象 所以建议还是传递一些常量或者方法
- 父组件
<template>
<div>
<h1>我是父组件</h1>
<br />
<p>--------------------------</p>
<br />
<child></child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: { child },
name: "ProjectFather",
// 方法一
provide: {
name: "oldCode",
},
// 方法二
provide() {
return {
name: "oldCode",
age: this.age, //this.data中的属性,
someMethod: this.someMethod, // methods 中的方法
};
},
data() {
return {
age: 20,
};
},
methods: {
someMethod() {
console.log("这是注入的方法");
},
},
};
</script>
};
</script>
- 子组件
<template>
<div>
<h1>我是子组件</h1>
<br />
<h1>{{ name }}</h1>
<h1>{{ age }}</h1>
</div>
</template>
<script>
export default {
inject: ["name"],
inject: ["name", "age", "someMethod"],
mounted() {
this.someMethod()
},
6、$attrs + $listeners
多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果**仅仅是传递数据,而不做中间处理,**使用 vuex 处理,未免有点大材小用。为此Vue2.4 版本提供了另一种方法 ---- $attrs / $listeners。
- $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=" $attrs"传入内部组件。通常配合 interitAttrs 选项一起使用。
- l i s t e n e r s :包含了父作用域中的 ( 不含 . n a t i v e 修饰器的 ) v − o n 事件监听器。它可以通过 v − o n = " listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=" listeners:包含了父作用域中的(不含.native修饰器的)v−on事件监听器。它可以通过v−on="listeners" 传入内部组件.
实现思路:
父组件定义name和age传给子组件,子组件使用v-bind=“ a t t r s " 的方式传给孙子组件,孙子组件使用 attrs"的方式传给孙子组件,孙子组件使用 attrs"的方式传给孙子组件,孙子组件使用attrs.name的形式使用name和age的值,子组件通过点击事件(@click=”$listeners.changeName")调用父组件的changeName方法,将name值修改为小李
- 父组件
<template>
<div>
<h1>我是父组件</h1>
<br />
<p>--------------------------</p>
<br />
<child :name="name" :age='age' @changeName="changeName"></child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: { child },
data() {
return {
age: 20,
name:'小李'
};
},
methods: {
changeName(){
this.name = '小明'
}
},
};
</script>
- 子组件
//child.vue
<template>
<div>
<h1>我是子组件</h1>
<br />
<h1>年龄:{{age}}</h1>
<el-button @click="$listeners.changeName">修改name</el-button>
<br/>
<p>------------------</p>
<br/>
<grandson v-bind='$attrs'></grandson> ------$attrs会传没有从props获取之外的属性给子子组件
</div>
</template>
<script>
import grandson from '../zjlianxi/grandson.vue';
export default {
components: { grandson },
props:['age'],----------------------------props接收的属性
};
</script>
- 孙组件
//grandson.vue
<template>
<div>
<h1>我是子子组件</h1>
<h1>{{$attrs.name}}</h1>
</div>
</template>
7、 EventBus
注意:
兄弟之间传值和子传父的方式类似,都是通过
$emit
事件发射的形式,但是兄弟之间要找一个Vue
实例作为媒介,也就是通过一个eventBus
事件总线
app.vue
<template>
<div class="container">
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
</div>
</template>
<script>
import myA from "./a.vue";
import myB from "./b.vue";
import myC from "./c.vue";
export default {
components: {
myA,
myB,
myC,
},
};
</script>
(1)局部
在src/components中新建一个Bus.js文件,然后导出一个空的vue实例
import Vue from 'vue'
export default new Vue;
组件
//a.vue 要传数据的一方
<template>
<div>
<h3>A组件:{{ name }}</h3>
<button @click="send">将数据发送给C组件</button>
</div>
</template>
<script>
import bus from '../../components/Bus' ------引入
export default {
data() {
return {
name: "tom",
};
},
methods: {
send() {
bus.$emit("data-a", this.name);
},
},
};
</script>
//b.vue 要传数据的一方
<template>
<div>
<h3>B组件:{{ age }}</h3>
<button @click="send">将数组发送给C组件</button>
</div>
</template>
<script>
import bus from '../../components/Bus' ------引入
export default {
data() {
return {
age: 20,
};
},
methods: {
send() {
bus.$emit("data-b", this.age);
},
},
};
</script>
!!!接收数据的一方
//c.vue
<template>
<div>
<h3>C组件:{{ name }},{{ age }}</h3>
</div>
</template>
<script>
import bus from '../../components/Bus' ------引入
export default {
data() {
return {
name: "",
age: "",
};
},
mounted() {
bus.$on("data-a", (name) => {
//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
this.name = name;
});
bus.$on("data-b", (age) => {
this.age = age;
});
},
};
</script>
(2)全局注册
在main.js中
Vue.prototype.$bus = new Vue()
组件
//a.vue 要传数据的一方
<template>
<div>
<h3>A组件:{{ name }}</h3>
<button @click="send">将数据发送给B组件</button>
</div>
</template>
<script>
export default {
data() {
return {
name: "tom",
};
},
methods: {
send() {
this.$bus.$emit("data-a", this.name); --------------this.$bus.$emit()
},
},
};
</script>
//b.vue 接收数据的一方
<template>
<div>
<h3>B组件:{{ name }}</h3>
</div>
</template>
<script>
export default {
data() {
return {
name: "",
};
},
mounted() {
this.$bus.$on("data-a", (name) => { ---------------------------- this.$bus.$on()
//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
this.name = name;
});
},
};
</script>
8、slot
插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot>
表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>
标签。
(1)简单示例
- 父组件
//father.vue
<template>
<div>
<h1>我是父组件</h1>
<br />
<p>--------------------------</p>
<child>
<div style="margin-top: 30px">我是子组件slot的内容</div>
</child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: { child },
};
</script>
- 子组件
//child.vue
<template>
<div>
<h1>我是子组件</h1>
<br />
<slot></slot>----------------------------slot
</div>
</template>
(2)具名插槽
父组件 :父组件通过 v-slot:[name] 的方式指定到对应的插槽中
子组件 :设置了两个插槽(header和footer):
- 父组件
//father.vue
<template>
<div>
<div>slot内容分发</div>
<child>
<template slot="header">
<p>我是页头的具体内容</p>
</template>
<template slot="footer">
<p>我是页尾的具体内容</p>
</template>
</child>
</div>
</template>
<script>
import child from "../zjlianxi/child.vue";
export default {
components: {
child
}
}
</script>
- 子组件
//child.vue
<template>
<div>
<div class="header">
<h1>我是页头标题</h1>
<div>
<slot name="header"></slot>
</div>
</div>
<div class="footer">
<h1>我是页尾标题</h1>
<div>
<slot name="footer"></slot>
</div>
</div>
</div>
</template>
9、vuex
https://blog.csdn.net/qq_55031668/article/details/128715292?spm=1001.2014.3001.5501
10、路由传值
方法一:
//father.vue
<template>
<div class="container">
<h1>我是父组件!</h1>
<button @click="details(2)">查看详情页</button>
</div>
</template>
methods: {
details(id){
this.$router.push({ path: "/a", query: { id: id, flag:'details' } });
},
},
//app.vue ------ 在mounted获取
mounted() {
this.Id = this.$route.query.id;
this.flag = this.$route.query.flag;
console.log(this.Id);
console.log(this.flag);
},
方法二
router.js 注意::id?—?代表可有可无
{
path: '/employee/detail/:id?', // 员工详情的地址
component: () => import('@/views/employee/detail.vue'),
hidden: true, // 表示隐藏在左侧菜单
meta: {
title: '员工详情' // 显示在导航的文本
}
}
//在页面中获取参数的中id
created() {
// 如何获取路由参数的中id
//可以打印下console.log(this.$route.params)
let id = this.$route.params.id
},
vue页面
<el-button size="mini" type="primary" @click="$router.push('/employee/detail')">添加员工</el-button>
<el-button size="mini" type="text" @click="$router.push(`/employee/detail/${row.id}`)">查看</el-button> ===>传参
11、.sync修饰符
父组件:接收子组件传过来的值
<!-- 表示会接收子组件的事件 update:showExcelDialog, 值 => 属性-->
<import-excel :show-excel-dialog.sync="showExcelDialog" />
子组件:
1、通过$emit(“update:showDialog”,false)把false传给父组件
<el-button size="mini" type="primary" @click="close">取消</el-button>
close(){
//修改父组件的值,子传父
this.$emit('update:showExcelDialog',false)
}
2、
<el-button size="mini" type="primary" @click="$emit('update:showDialog', false)">取消</el-button>