由于 <el-dialog>里面的业务逻辑比较复杂, 我们想把dialog单独封装一个组件,这样由父组件控制dialog显示隐藏。接下来我们看看怎么封装的?
场景
我们有这样一个简单的父子组件通信的场景
<!--parent component-->
<my-dialog :visible="myVisible"></my-dialog>
<el-button @click="myVisible = !myVisible">toggle</el-button>
data(){
return {
myVisible: false
}
}
<!--child component-->
<el-dialog :visible.sync="visible">hello vue !</el-dialog>
props: {
visible:false
}
讲道理,按照上述场景的代码,我们点击按钮就可以控制按钮的显示隐藏了。奈何,现实总是不尽人意。
发现了两个问题:
- 一个警告
- 一个bug:点击按钮一次弹框显示,再点一次弹框隐藏,接下来问题就出现了,后续每次要点击两次按钮才能显示弹框。为啥呢?
首先来解释第一个问题:父组件通过props将数据流向到子组件,反过来不行。如果我们要改变props就在子组件外面改变好了。如果在子组件里面修改的话,数据流向就难以理解了。这里可以参考单向数据流的解释。
回到场景里,我们没有在子组件里做props修改,为什么props会改呢?原因出现在.sync这个修饰符上,这里是官网的链接.sync。.sync是双向绑定的,当el-dialog隐藏时,同时会更新props的visible属性。所以就修改了我们传入的visible的值。
那么,我们就可以回答第二个问题了,既然visible在子组件里更新了,我们父组件里的visible还是原来的,多点一次按钮就相当于父子组件的visible值同步了。所以再次点击时,弹框就能正常显示了。
如何解决这两个问题呢?
方案一:使用.sync
基于场景做下改造, 给属性添加一个修饰符
.sync, 子组件使用computed对数据做获取和更新。
<!--parent component-->
<my-dialog :visible.sync="myVisible"></my-dialog>
<el-button @click="myVisible = !myVisible">toggle</el-button>
data(){
return {
myVisible: false
}
}
<!--child component-->
<el-dialog :visible.sync="dialogVisible">hello vue</el-dialog>
<!--既然props的值不能被修改,那我们只做获取就好了,我们按照警告里将它改造为computed-->
props: {
visible:false
},
computed:{
dialogVisible:{
// 那我们只做获取就好了
get(){
return this.visible
},
set(v){
// 当子组件里的dialogVisible被修改时,通知父组件同步状态
this.$emit('update:visible', v)
}
},
}
方案二:使用v-model
基于场景做下改造, v-model, 延续方案一,子组件使用
computed对数据做获取和更新。
<!--parent component-->
<my-dialog v-model="myVisible"></my-dialog>
<el-button @click="myVisible = !myVisible">toggle</el-button>
data(){
return {
myVisible: false
}
}
<!--child component-->
<el-dialog :visible.sync="dialogVisible">hello vue</el-dialog>
<!--既然props的值不能被修改,那我们只做获取就好了,我们按照警告里将它改造为computed-->
<!--写法一-->
props: {
visible: Boolean
},
// 自定义model
model:{
prop: 'visible',
event: 'update:visible'
},
computed:{
dialogVisible:{
// 那我们只做获取就好了
get(){
return this.visible
},
set(v){
// 当子组件里的dialogVisible被修改时,通知父组件同步状态
this.$emit('update:visible', v)
}
},
}
<!--写法二 使用默认model-->
props: {
value: Boolean
},
computed:{
dialogVisible:{
// 那我们只做获取就好了
get(){
return this.value
},
set(v){
// 当子组件里的dialogVisible被修改时,通知父组件同步状态
this.$emit('input', v)
}
},
}