<el-dialog>想封装起来 :visible.sync怎么处理?

2,154 阅读2分钟

由于 <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
}

讲道理,按照上述场景的代码,我们点击按钮就可以控制按钮的显示隐藏了。奈何,现实总是不尽人意。

发现了两个问题:

  • 一个警告

image.png

  • 一个bug:点击按钮一次弹框显示,再点一次弹框隐藏,接下来问题就出现了,后续每次要点击两次按钮才能显示弹框。为啥呢?

首先来解释第一个问题:父组件通过props将数据流向到子组件,反过来不行。如果我们要改变props就在子组件外面改变好了。如果在子组件里面修改的话,数据流向就难以理解了。这里可以参考单向数据流的解释。

回到场景里,我们没有在子组件里做props修改,为什么props会改呢?原因出现在.sync这个修饰符上,这里是官网的链接.sync.sync是双向绑定的,当el-dialog隐藏时,同时会更新propsvisible属性。所以就修改了我们传入的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)
        }
    },
    
}