目录
一.反向传值(子组件传值给父组件)
我们经常在网页中遇到如图所示的模态窗,我们想要达到的效果是,点击右上方的关闭按钮,模态窗消失,在vue中我们想让一个元素从页面中隐藏起来,可以采用v-if或者v-show,将值从true(显示)改为false(隐藏)。
那么我们的思路就是点击关闭按钮,flag的值,代码解释如下
对关闭按钮绑定点击事件,@click=“clear” 这里的事件绑定在子组件上面,而我们想要达到的效果是改变父组件中的数据(数据驱动页面,页面中模态窗口的显示和隐藏由数据flag决定),
首先我们采用属性传值将flag:true传给子组件
要注意,这里涉及到知识点单项数据流,数据只能由父级组件流向子级主键,修改属性值,只会让子组件刷新,父组件的数据没有更新。解释一下父组件数据传给子组件(属性传值),子组件传给父组件(反向传值)
我们想要解决这个问题,想要改变父组件中的值方法如下(反向传值):
因为数据流是单向的:子组件触发事件 this.$emit("事件名",数据1,数据2,````),父组件绑定事件 @事件名="监听器"
1.在父组件上绑定事件,此事件非官方由自己设计作用是啥
2.子组件中触发自定义事件 this.$emit("第一个参数为触发事件的名字","第二个参数为传给父组件的数据")
3.将子组件传给父组件的值,对父组件中的数据进行更改(反向传值)
代码解析如下:
父组件代码:
子组件代码:
代码也发出来,大家可以去实现一下哟:
父组件:
<template>
<div>
<img class="img" src="https://img0.baidu.com/it/u=3443901670,2726678779&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=281"
alt="">
<span>父组件中flag的值---{{ flag }}</span>
<Motail v-if="flag" :a1="flag" @updata="fn"></Motail>
</div>
</template>
<script>
import Motail from "@/motai/Motail.vue"
export default {
components: {
Motail,
},
data() {
return {
flag: true
}
},
methods: {
fn(arg) {
// 事件传进来的参数值为事件触发子组件传给父组件的值
console.log(arg, "1111111111111");
this.flag = arg;
}
}
}
</script>
<style>
.img {
width: 100%;
height: 100%;
}
</style>
子组件:
<template>
<div class="Motail">
<div class="box">
<div>
<span>这是弹出的模态窗</span><br>
<span>打印父组件传过来的a1值---{{ a1 }}</span>
</div>
<div class="x" @click="clear1">×</div>
</div>
</div>
</template>
<script>
export default {
props:["a1"],
methods:{
clear1(){
// 给关闭按钮绑定点击事件
// 想办法把父组件中的数据源修改了
// 数据驱动页面,页面中模态窗口的显示和隐藏由数据决定的(flag:true)
console.log(this.a1);//打印父组件的传过来的flag的值
//this.a1="false";
// 触发自定义事件
// 第一个参数update为触发事件的名字
// 第二个参数为传给事件的数据(子组件传给父组件)
this.$emit("updata",false)
}
}
}
</script>
<style>
.Motail {
width: 100vw;
height: 100vh;
background-color: rgba(112, 112, 112, 0.5);
position: fixed;
z-index: 20;
top: 0px;
right: 0px;
display: flex;
justify-content: center;
align-items: center;
}
.box {
width: 300px;
height: 200px;
font-weight: 30px;
font-weight: 800;
background-color: white;
position: relative;
border-radius: 10px;
line-height: 100px;
text-align: center;
}
.x {
height: 30px;
width: 30px;
background-color: rgb(116, 119, 121);
border-radius: 50%;
color: white;
font-weight: 800;
font-size: 30px;
text-align: center;
line-height: 30px;
position: absolute;
top: 0px;
right: 0px;
}
</style>
二.$refs
在vue中,并不建议我们去做DOM操作,但是却提供了DOM操作的方法
vue提供的DOM操作的接口,元素或者组件上绑定ref="标记",组件中this.$refs.标记获取
如果我们要获取某个具体的元素,直接对象获取值方法 , 例如:this.$refs.p1获取ref=“p1”的该标签。
ref 除了获取页面上的元素之外还可以获取组件
例如如下:
这里VueComponent表示该引用数据的构造函数,为vue框架中的类,组件对象由VueCompoent创建。
三.$parent
概括:当前组件的父组件,父组件还可以链式调用获取父组件,根组件没有父组件
在本章博客中一讲到,子组件给父组件传数据,修改父组件的数据较为麻烦,这里介绍一下$parent,可直接修改父组件的数据,省去了绑定事件,触发事件等步骤。
this.$parent中this表示当前组件,.$parent表示当前组件的父组件,可直接this.$parent.flag进行更改父组件中data中的flag值
扩展:App的父组件为Vue 也就是说根组件为Vue,根组件的构造函数不是VueCompoent,而是Vue。根组件的父组件为undefined
四.$children
概括:是一个数组,数组中是当前组件的子组件们,不包括子元素,不保证顺序
同三,可以用$children访问子组件,一个子组件有且仅有一个父组件,一个父组件可以有很多子组件。简单点说就是一个儿子只能有一个爸爸,而一个爸爸可以有很多个儿子。
既然我们说到一个父组件的子组件不止一个,则返回的就不是一个对象而是一个数组
$children也有很多弊端,比如我们在this.$children[0] 取到第一个子组件,如果我们将第一个子组件v-if="false"改为哦v-for="true",该组件不一定是第一个(按照时间添加到当前组件的最后,与添加的时间有关),这里取到的顺序与我们书写代码的顺序无关。所以我们不建议用$children来获取子组件
五.$attrs/$listeners -----多层传值
在几重父子关系中传值用反向传值的方法十分繁琐,为了解决这个问题有了多层传值(过渡作用)
attrs 为英语单词属性sttributes的缩写,表示该组件的父组件传的所有属性在该组件下都不会使用,直接传给下一级组件用(图片中表示传给Box2组件),传给Box2过后,再用props:[""]接收。
如果想要修改父组件中的数据,用一中触发事件实现(也就是this.$emit("事件名",数据1,数据2,````)),这里呢也需要添加v-on="$listeners"来起传递作用,监听器的传递。
例子:
App------>Box1-------->Box2
App代码:
<template>
<div>
<p>app-----{{ msg }}</p>
<Box1 :a1="msg" @change="fn"></Box1>
</div>
</template>
<script>
import Box1 from "@/Box/Box1.vue"
export default {
data(){
return {
msg:"app组件的数据"
}
},
methods:{
fn(arg){
console.log(arg,1111111111);//arg表示box2传回来的数据
this.msg=arg;
}
},
components:{
Box1,
}
}
</script>
<style>
</style>
App的子组件Box1的代码:
<template>
<div>
<Box2 v-bind="$attrs" v-on="$listeners"></Box2>
</div>
</template>
<script>
import Box2 from "@/Box/Box2.vue"
export default {
data(){
return {
msg:"app组件的数据"
}
},
components:{
Box2,
}
}
</script>
<style>
</style>
App的子组件Box1的子组件Box2代码:
<template>
<div>
<p>box2----{{ a1 }}</p>
<button @click="fn1">box2-btn</button>
</div>
</template>
<script>
export default {
props: ["a1"],
methods:{
fn1(){
console.log(this.a1);
this.$emit("change","这是box2传给App的数据")
}
}
}
</script>
<style>
</style>
六.$root ----根组件
this.$root可以获取到根组件,每个vue框架中有且只有一个根组件 ,我们可以将数据放到根组件中,避免的子父组件之间传值的繁琐。
在之后的使用中我们可以用$rooot 来对兄弟组件进行传值
七.依赖注入 provide/inject
依赖注入比多层传值更加简洁,多层传值中间组件需要写代码v-bind="$attrs" v-on="$listeners"进行过渡。依赖注入弊端不能修改提供者提供的数据。所有组件都可以成为provide的提供者,inject为提供者的后代
provide提供数据
<script>
export default {
provide(){
return{
msg2:"app组件提供的数据"
}
}
}
</script>
inject接收数据
<script>
export default{
inject:["msg2"]
}
</script>
八.v-model
在之前的学习阶段,我们知道v-model是语法糖,同样的这里也可以使用
在一中,我们将 :a1改为 :value, 将 @updata改为@input,
可以改写成以下格式
<Box v-model="flag"></Box>
v-model可以省去下面这一步,也就是得到事件触发传过来的值对当前组件的值进行更改。
v-model="flag"的底层就是2个语法的合成
1.属性绑定 :value="flag"
2.绑定了@input还是函数中把接收的数据设置给了this.flag
v-model是下述的一个特例(省去了事件绑定步骤)
九.中央事件总线
在$root中提到可以在根组件vue上进行操作,但是在渲染网页的vue上去各种各样的绑定会对性能造成影响。
解决方案:新建vue,在新建的vue上进行各种绑定。通过创建一个新的vm对象,专门统一注册事件,供所有组件共同操作,达到所有组件随意隔代传值的效果
使用例子:
this.$bus.emit
this.$bus.on