发现问题

在vue项目中,父子组件数据传递是最常见的场景,但是今天在开发过程中父级数据传递到子组件,控制子组件的显隐,发现存在问题,一直监听不到数据的变化,详细排查后发现是因为props在传递数据的时候子组件接收到数据后没有及时发生变化导致的。

这里使用的是 iview 框架

// 子组件
<template>
    <Modal
        v-model="isShowModel"
        title="测试弹窗">
        <p>Content of dialog</p>
        <p>Content of dialog</p>
        <p>Content of dialog</p>
    </Modal>
</template>

<script>
export default {
    name: 'TestModel',
    props: {
        showModel: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            isShowModel: this.showModel
        }
    }
}
</script>
// 父组件
<template>
  <div id="app">
    <TestModel :showModel="showModel"></TestModel>
    <Button type="primary" @click="changeShowModel">显示弹窗</Button>
  </div>
</template>

<script>
import TestModel from './components/TestModel.vue'

export default {
  components: {
    TestModel
  },
  data() {
    return {
      showModel: false,
    }
  },
  methods: {
    changeShowModel() {
      this.showModel = true;
    }
  }
}
</script>

问题分析

预期效果是点击按钮直接弹出弹窗,结果怎么也弹不出来。子组件中增加 watch 监听 props 发现数据已经变化了,但是 data 中接收 props 数据的变量并未改变,发现问题所在。

问题原因就是接收 props 数据的变量无法及时响应 props 的变化,下一步解决问题就是重点解决响应变化问题。

问题解决

解决方案1

子组件中增加 watch 监听 props 中传进来的数据变化,同时当子组件数据变化时,数据上传父组件。

// 子组件
<template>
    <Modal
        v-model="isShowModel"
        width="1000"
        title="测试弹窗"
        @on-cancel="cancel"    
    >
        <p>Content of dialog</p>
        <p>Content of dialog</p>
        <p>Content of dialog</p>
    </Modal>
</template>

<script>
export default {
    name: 'TestModel',
    props: {
        showModel: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            isShowModel: this.showModel
        }
    },
    watch: {
        showModel(v) {
            this.isShowModel = this.showModel
        }
    },
    methods: {
        cancel() {
            this.$emit('cancel', this.isShowModel);
        }
    }
}
</script>
// 父组件
methods: {
    modelCancel(v) {
        this.showModel = v;
    }
}

解决方案2

使用 vue 中的计算属性( computed )中的 set 方法。

// 子组件
<template>
    <Modal
        v-model="isShowModel"
        width="1000"
        title="测试弹窗"
    >
        <p>Content of dialog</p>
        <p>Content of dialog</p>
        <p>Content of dialog</p>
    </Modal>
</template>

<script>
export default {
    name: 'TestModel',
    props: {
        showModel: {
            type: Boolean,
            default: false
        }
    },
    computed: {
        isShowModel: {
            set(v) {
                this.$emit('cancel', v);
            },
            get() {
                return this.showModel;
            }
        }
    },
}
</script>

有兴趣的兄弟可以关注我的博客 www.maple.ink
个人博客

Logo

前往低代码交流专区

更多推荐