3种方式搞定Vue子组件修改属性报错


目的

Vue里是单向数据流,因此父组件传递子组件的参数,不能修改。如果尝试去修改,将会报错如下:vue.runtime.esm.js?2b0e:587 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "dialogVisible",本文目的就是解决这个报错。

效果

双向通信

实现方式1

用$refs直接访问子组件的属性,由于读写的都是子组件本身,因此双向通信的问题一扫而光,不再报错。

父组件代码

1
2
3
4
5
6
7
<el-button type="danger" @click="alert">$refs的方式</el-button>
<my-alert ref="alert" />
methods: {
    alert() {
      this.$refs.alert.visual = true
    }
}

子组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  <el-dialog
    title="refs的方式"
    :visible="visual"
    :before-close="handleClose"
  >
  </el-dialog>
</template>
<script>
export default {
  data() {
    return {
      visual: false,
    }
  },
  methods: {
    handleClose() {
      this.visual = false
    }
  }
}
</script>

实现方式2

使用props由父组件传值给子组件,这是正统做法,会稍稍麻烦一些,需要作双向通信

父组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<el-button type="success" @click="alert2">prop的方式</el-button>
<your-alert :visual="visual" @hide="didHide" />
...
  data() {
    return {
      visual: false
    }
  },
methods: {
    alert2() {
      this.visual = true
    }
}

这里通过visual参数,传递参数给子组件,同时@hide事件回调等待子组件传参回来

子组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
  <el-dialog
    title="prop的方式"
    :visible="dialogVisible"
    :before-close="handleClose"
  >
  </el-dialog>
</template>
<script>
export default {
  props: ['visual'],
  data() {
    return {
      dialogVisible: this.visual
    }
  },
  watch: {
    visual(val) {
      this.dialogVisible = val;
    }
  },
  methods: {
    handleClose() {
      console.log('is closing')
      const state = false
      this.dialogVisible = state
      // 向父组件传值
      this.$emit('hide', state)
    }
  }
}
</script>

首先子组件通过props定义了一个属性,用来接收父组件的传参,props: ['visual'],由于它不能被修改,会报文章开头提示的错误,详细说明见Vue官方文档,https://cn.vuejs.org/v2/guide/components-props.html,所以定义了一个dialogVisible用来复制它,然后对它就进行就不会影响父组件值,于是不会报错了。

这里还需要watch监听传来的props值,来改变我们子组件局部变量,即时刷新界面,也就是这里的弹窗才可以弹出。

最后,当点击关闭后,要通过你父组件更新其visual的值为false,需要关闭后,无法通过再次点按钮弹出窗口,父组件会误以为visual一直是true。

实现方式3

由于这个例子就是要控制弹窗的显示与关闭,那么大可不必这么麻烦,子组件包含button的方式,visual的可见性true|false全凭子组件负责,已经不存在通信的问题了,比第一种方式还要简单粗暴。

父组件代码

1
<her-alert />

子组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
  <div>
    <el-button type="primary" @click="show">子组件包含button的方式</el-button>
    <el-dialog
      title="子组件包含button的方式"
      :visible="dialogVisible"
      :before-close="handleClose"
    >
    </el-dialog>
  </div>
</template>
<script>
export default {
  data() {
    return {
      dialogVisible: false
    }
  },
  methods: {
    show() {
      this.dialogVisible = true
    },
    handleClose() {
      this.dialogVisible = false
    }
  }
}
</script>
<style scoped>
div {
  margin-top: 20px;
}
</style>

总结

第1种,是通用解

第2种,早晚要派上用场

第3种,是特殊场景解法

关注我

欢迎关注订阅号【黄秀杰】

mp