前言:如果遇到那种页面很复杂并且逻辑要写很多的页面,但这个页面整体只有一个提交按钮。
这个时候组件封装就很有必要,整个界面只有一个提交按钮,这时候组件通信就暴露出来了,我子组件的数据该在什么事件触发时传递子组件的值。
这里涉及了,js代码执行顺序、js的事件循环(主进程、宏任务、微任务)、父子组件的生命周期、ref与refs。
1.js代码执行顺序
js代码的执行顺序永远是从上往下执行的。
2.js的事件循环
js代码虽然是从上往下执行的,但如果碰到异步或者定时器就另当别论了,js代码在按照顺序执行时,会先执行主进程(变量、常量、事件函数函数),执行完后会去检查代码中有没有宏任务或者微任务,如果都有那么会先执行微任务(异步任务),此时也会按照先后顺序执行,最后会执行宏任务(定时器)。
3.父子组件的生命周期
加载渲染过程
- 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
- 父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
- 父beforeUpdate->父updated
销毁过程
- 父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
4 vue中ref与refs
在组件本身声明ref字符即可通过refs获取组件内所有方法
5.新增/修改
新增
把需要封装的界面创建一个新的vue文件进行书写,此时在父组件对书写好的子组件进行挂载,假设我们封装了两个组件,但整个页面只有确认一个事件方法可以提交参数,可以通过$refs
获取子组件的所有实例,父组件在合适的位置代替触发传递参数的方法
修改
以下是解释为什么子组件要监听父组件传输数据是否完成:
因为新增界面只需把自己的传值方法与异步方法交给父组件代替执行,所以不会出现数据触发时机的问题
但是,修改时一般都是先获取接口传递的数据,这时候父组件会执行一个异步的事件,这时候我们子组件不管是在created ()
还是mounted ()
钩子中都是获取不到父组件传递过来的参数,这个时候就很尴尬,值在props
中确实接收到了父组件的参数,但我在钩子中就是获取不到
如果听不懂可以看上面的父子组件生命周期,你会发现子组件的生命周期全部结束后父组件才完成最后的加载,举个例子:就好比子组件的数据都全部更新完成了,父组件还在更新中
所以,我们可以监听数据是否传递完成来解决,如果用用户网络比较差,我们可以用v-loading
遮罩层提升体验
//父组件
<template>
// 在点新增/保存时会发现子组件的所有实例被销毁又重新创建了
// 如果用户输入某个值错误此时就会发生所有已输入的值被清空了,这并不是我们期望的结果
// 此时我们应当保存子组件的状态,利用<keep-alive></keep-alive>标签去进行页面缓存实现
<keep-alive>
<子组件名 :子组件接收='tabberList' ref='ref自定义值' @自定义事件名='自定义事件名'></子组件名>
</keep-alive>
<keep-alive>
<子组件名1 :子组件接收1='tabberList' ref='ref自定义值1' @自定义事件名1='自定义事件名1'></子组件名1>
</keep-alive>
<el-button type="primary" @click="sumbit">确认</el-button>
</template>
import 子组件名 from '子组件路径'
import 子组件名1 from '子组件路径'
components: {
子组件名,
子组件名1
},
data () {
return {
tabberList:[], // 假设这是获取详情得到的数据
子组件值:'',
子组件值1:''
}
},
methods:{
async getList(){
this.tabberList=await 请求详情接口
}
async sumbit(){
// 利用 Promise.all 确保方法执行的顺序
await Promise.all([
// 获取子组件的所有实例并代替触发传值事件
this.$refs.ref自定义值.transmit()
// 获取子组件1的所有实例并代替触发传值事件
this.$refs.ref自定义值1.transmit()
// 触发提交页面信息
this.addSumbit()
])
},
// 子组件传递自己区域的值
自定义事件名(val){
this.子组件值=val
}
// 子组件传递自己区域的值
自定义事件名1(val){
this.子组件值1=val
}
// 新增/修改保存事件
async addSumbit(){
await //调用接口
}
}
子组件
<template>
<div class="" v-loading="loading">
...
...
</div>
</template>
props: {
子组件接收: {
default: () => []
}
},
data(){
return{
loading:true
list:{ // 假设这就是所有html绑定的数据
...
...
}
}
}
watch: {
// 监听数据是否传递完成
//如果不明白为什么要监听数据传输是否完成请看标题下面的解释
子组件接收(val) {
this.list= val
this.loading = false
}
},
// 向父组件传递参数事件
// 特别注意,如果页面有表单校验的话要把校验的结果也传递过去,不然会发生未传正确/符合要求的参数,但提交接口通过的尴尬事情
methods:{
transmit(){
this.$emit('自定义事件名',this.list)
}
}
子组件1
<template>
<div class="" v-loading="loading">
...
...
</div>
</template>
props: {
子组件接收1: {
default: () => []
}
},
data(){
return{
loading:true
list:{ // 假设这就是所有html绑定的数据
...
...
}
}
}
watch: {
// 监听数据是否传递完成
//如果不明白为什么要监听数据传输是否完成请看标题下面的解释
子组件接收1(val) {
this.list= val
this.loading = false
}
},
data(){
return{
list:{ // 假设这就是所有html绑定的数据
...
...
}
}
}
// 向父组件传递参数事件
// 特别注意,如果页面有表单校验的话要把校验的结果也传递过去,不然会发生未传正确/符合要求的参数,但提交接口通过的尴尬事情
methods:{
transmit(){
this.$emit('自定义事件名1',this.list)
}
}