Vue 组件通信及进阶语法
文章目录
一、scoped 样式冲突
注意点:
① 结构:只能有一个根元素;
② 样式:组件中定义的样式默认是全局生效的,会影响到所有组件,而 scoped 属性下的样式可以作用于当前组件;
③ 逻辑:el 是根实例所独有的,data 是一个函数。
默认情况下,写在组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题,而组件应该有着自己独立的样式,我们可以给组件加上 scoped 属性,让样式只作用于当前组件。
<style scoped>
</style>
scoped 原理:给当前组件中的所有元素都添加一个自定义属性(data-v-hash),不同的哈希值用于区分不同的组件!
二、data 是一个函数
在一个组件里面,data 选项必须是一个函数,保证每个组件实例维护独立的一份数据对象。 每次创建新的组件实例,都会执行一次 data 函数,得到一个新对象,各个对象之间互不影响。
三个实例对象各自独立,互不影响!
三、组件通信
组件通信就是指组件与组件之间的数据传递。 组件之间的数据是独立的,无法直接访问其他组件的数据,想要用其他组件的数据,就必须要组件通信。
组件关系分为父子关系和非父子关系:
1. 父子通信
父组件通过 props 将数据传递给子组件,子组件不能直接修改父组件中的数据,而是利用 $emit 通知父组件进行修改更新。
<!--App.vue-->
<template>
<div class="App">
<Son :title="myTitle" @changeTitle="handleChange"></Son>
</div>
</template>
<script>
import Son from './components/Son.vue';
export default {
data() {
return {
myTitle: '栈老师不回家'
}
},
components: {
Son
},
methods: {
handleChange(newTitle) {
this.myTitle = newTitle
}
}
}
</script>
<style scoped>
.App {
width: 600px;
height: 600px;
background-color: rgb(193, 230, 248);
margin: 0 auto;
padding: 20px;
}
</style>
<!--Son.vue-->
<template>
<div class="son">
{{ title }}
<button @click="changeFn">修改</button>
</div>
</template>
<script>
export default {
props: ['title'],
methods: {
changeFn() {
this.$emit('changeTitle', '新数据')
}
}
}
</script>
<style>
.son {
width: 200px;
height: 200px;
background-color: #f8dd30;
margin: 0, auto;
}
</style>
通过自定义属性和自定义事件来实现父子之间的通信。子组件通过 $emit 向父组件发送消息通知,第一个参数是父组件中自定义的监听事件,第二个参数是修改的新数据,父组件通过 @change Title 监听消息,并做出回应,即修改数据。
1.1 props 校验
props 用于向子组件传递数据,并且可以传递任意类型、任意数量的数据。
为了保证组件的正常运行,组件的 props 不可以乱传。我们可以给组件的 props 指定验证要求,不符合要求,控制台就会有错误提示,可以帮助开发者快速发现错误。
① 类型校验
props: {
校验的属性名: 类型 //Number、String、Boolean、Array、Object...
}
② 更详细的校验
props: {
校验的属性名: {
type: 类型, //Number、String、Boolean、Array、Object...
required: true, //是否必填
default: 默认值, //默认值
validator(value) { //value就是传过来的属性名
//自定义校验逻辑
return true //true通过校验,false不通过校验
}
}
}
1.2 props 比较 data
共同点:都可以给组件提供数据。
区别:data 的数据是自己的,可以随便改;props 的数据是外部的,不能直接改,要遵循单向数据流原则,通知其父组件进行修改。
单向数据流:父组件的 props 更新,会单向向下流动,从而影响到子组件。
count++ 是 count 加 1 并重新赋值,会直接修改 count 的值,而 count + 1 并不会重新赋值,count 的值也不会被改变,所以对于 props 里的数据,不要使用 ++,直接让它 + 1 通知父组件即可!
谁的数据谁负责,数据一般提供在公共的父组件中!
2. 非父子通信
2.1 event bus
event bus 用于非父子组件之间,进行简易的消息传递。
① 创建一个双方都能访问到的事件总线(空 Vue 实例)→ utils/EventBus.js
import Vue from 'vue'
const Bus = new Vue()
export default Bus
② A 组件(接收方),监听 Bus 实例的事件
created() {
Bus.$on('sendMsg', (msg) => {
this.msg = msg
})
}
③ B 组件(发送方),触发 Bus 实例的事件
Bus.$emit('sendMsg', '这是一个消息')
发送方发送消息,从而触发 Bus,而接收方时刻监听着 Bus,Bus 一旦被触发,接收方立马就可以接收到消息!
完整代码如下:
//EventBus.js
//创建一个都能访问到的事件总线(空的Vue实例)
import Vue from "vue";
const Bus = new Vue()
export default Bus
//BaseA.vue
<template>
<div class="baseA">
我是A组件(接收方)
<div>{{ msg }}</div>
</div>
</template>
<script>
import Bus from '@/utils/EventBus';
export default {
data() {
return {
msg: ''
}
},
created() {
//订阅消息
Bus.$on('sendMsg', (msg) => {
this.msg = '收到消息了:'+ msg
})
}
}
</script>
<style scoped>
.baseA {
width: 200px;
height: 150px;
padding: 10px;
margin-top: 10px;
border: 3px solid #000;
border-radius: 5px;
font-size: 20px;
}
</style>
<template>
<div class="baseB">
我是B组件(发布方)
<button @click="publish">发布通知</button>
</div>
</template>
<script>
import Bus from '@/utils/EventBus';
export default {
methods: {
publish() {
//发送方通过触发事件的方式发布消息
Bus.$emit('sendMsg', '栈老师回家')
}
}
}
</script>
<style scoped>
.baseB {
width: 200px;
height: 150px;
padding: 10px;
margin-top: 10px;
border: 3px solid #000;
border-radius: 5px;
font-size: 20px;
}
</style>
发送方发送的消息,可以被多个接收方订阅!
2.2 provide-inject
跨层级(孙子和爷爷)之间的通信用 provide-inject。
//发送方,爷爷
provide() {
return {
color: this.color, //简单类型,非响应式
userInfo: this.userInfo //复杂类型,响应式
}
},
data() {
return {
color: 'blue',
userInfo: {
name: '栈老师',
age: 20
}
}
}
//接收方,孙子
inject: ['color', 'userInfo']
使用 provide-inject 共享数据的时候,通常以复杂类型的形式,把数据包装成一个对象进行传递,这样可以保证数据的响应式。
四、进阶语法
1. v-model 详解
原理:v-model 本质上是一个语法糖,例如应用在输入框上,就是 value 属性和事件的合写。
作用:提供数据的双向绑定。
注意:$event 用于在模版中获取事件的形参。
<!--两个input作用一样-->
<div class="App">
<input v-model="msg" type="text">
<input :value="msg" @input="msg=$event.target.value" type="text">
</div>
$event.target.value 就是输入框中的值!
表单类组件封装:
① 父传子:数据应该是父组件 props 传递过来的,v-model 拆解绑定数据。
② 子传父:监听输入,子传父传值给父组件修改。
应用场景举例:此下拉框中所有的数据都来自它的父组件,所以数据是不能直接修改的,子组件中也不能使用 v-model,因为 v-model 会修改数据,解决办法就是拆解 v-model,让 value 和 @change 拆开写。
<select :value="cityId" @change="handleChange">
...
</select>
props: {
cityId: String
},
methods: {
handleChange(e) {
this.$emit('父组件中的事件名', e.target.value)
}
}
<!--父组件接收-->
<BaseSelect :cityId="selectId" @事件名="selectId=$event"/>
2. sync 修饰符
作用:可以实现子组件与父组件数据的双向绑定,简化代码。
特点:props 属性名可以自定义,非固定为 value。
场景:封装弹框类的基础组件,visible 属性,true 显示,false 隐藏。
:属性名.sync,本质上就是 :属性名和 @update:属性名的合写。
<!--父组件与子组件通过:visible.sync即可实现数据的双向绑定-->
<BaseDialog :visible.sync = "isShow"/>
//子组件正常用props接收数据,emit发出修改申请
props: {
visible: Boolean
},
methods: {
close() {
this.$emit('update:visible', false) //'update:属性名', 修改后的数据
}
}
3. ref 和 $refs
作用:ref 和 $refs 用于获取 dom 元素或组件实例。
查询范围:当前组件内(更精准确定)。
① 获取 dom 元素
<!--给目标标签添加ref属性-->
<div ref="chartRef">我是渲染图标的容器</div>
//dom渲染完成后(mounted),通过this.$refs.属性值,获取目标标签
mouted() {
console.log(this.$refs.chartRef)
}
② 获取组件
<!--给目标组件添加ref属性-->
<BaseForm ref="baseForm">我是一个组件</BaseForm>
//使用this.$refs.baseForm可以获取目标组件
this.$refs.baseForm.组件方法()
获取到目标组件后,就可以直接调用组件对象里面的方法!
4. $nextTick
需求:点击编辑,编辑框自动显示并聚焦。
this.isShowEdit = true //显示输入框
this.$refs.inp.focu() //获取焦点
问题:以上代码并不能立刻获取焦点。
原因:Vue 是异步更新(提升性能)。并不会读一行就执行一行,它要等全部代码都读完了才开始执行,所以此处 dom 还没更新出来,就去获取焦点,显然是不可能成功的。
$nextTick:等 DOM 更新后,才会触发执行此方法里的函数体。
将获取焦点的操作放在 $nextTick 里面就可以了:
this.$nextTick(() => {
this.$refs.inp.focus()
})
原文地址:https://blog.csdn.net/m0_52861684/article/details/143713025
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!