BeginMan blog

理解vue 组件数据传递

多看vue文档是很有必要的,对于这篇文章来说,把官方文档组件 不翻个熟透是难以理解的。这段时间要写点前端了,从远古时期的jquery到现代vue,迭代更新的东西挺多的。我在写vue的时候总是困惑在组件问题上,昨晚大致梳理了一遍,写博记之。

在写vue遇到的问题有:

[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: “result” (found in component )

这是因为vue2去除父子组件的数据双向绑定,子组件不能直接修改父组件的属性。解决方法就是子组件data定义父组件传过来的副本,利用 $emit方法回传过去。

这个问题是我用Element这个库遇到的,有一个.sync绑定修饰符可以实现props的双向绑定,但是在vue2这个功能已经废弃了,在Vue2中组件的props的数据流动改为了只能单向流动,即只能由组件外(调用组件方)通过组件的DOM属性attribute传递props给组件内,组件内只能被动接收组件外传递过来的数据,并且在组件内,不能修改由外层传来的props数据。prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态

图片来源:https://juejin.im/entry/5843abb1a22b9d007a97854c

如上组件内props: props: ["result"]

解决方案流程:

  1. 组件data内创建props属性副本, myResult: this.result
  2. watch监听props属性并同步到副本
  3. watch监听副本,通知到组件外

流程图如下:

图片来源:https://juejin.im/entry/5843abb1a22b9d007a97854c

项目代码描述:

父组件:

<template>
...
<comDialog :dialogVisible="dialogVisible"  @changeVisible="changeVisible" :msg="msg"></comDialog>
</template>

import comDialog from '../../components/dialog.vue'

export default {
        data () {
          return {
            dialogVisible: false,
            msg: '',
          }
        },
        components: {
          comDialog
        },
        methods: {
          submitForm (formName) {
            this.$refs[formName].validate((valid) => {
              if (valid) {
                console.log('submit!')
                // 变更属性,通知子组件
                this.dialogVisible = true
                this.msg = '项目创建成功!'
              } else {
                console.log('error submit!!')
                return false
              }
            })
          },
          resetForm (formName) {
            this.$refs[formName].resetFields()
          },
          // 定义方法,接收子组件传递的事件,事件指定某个方法
          // 外层调用组件方注册变更方法,将组件内的数据变更,同步到组件外的数据状态中
          changeVisible: function (val) {
            console.log('i got it', val)
            this.dialogVisible = val
          }
        }
}

子组件:

<template>
<el-dialog
  title="提示"
  v-model="dialogStatus"
  size="tiny"
  :before-close="handleClose">
  <span></span>
  <span slot="footer" class="dialog-footer">
    <el-button @click="dialogStatus = false"> </el-button>
    <el-button type="primary" @click="dialogStatus = false"> </el-button>
  </span>
</el-dialog>
</template>

<script>
  export default {
    props: [
      'msg',
      'dialogVisible'
    ],
    data () {
      return {
        // props副本
        dialogStatus: this.dialogVisible
      }
    },
    watch: {
      // 监听props属性
      // 监听外部对props属性dialogVisible的变更,并同步到组件内的data属性dialogStatus副本属性中
      dialogVisible (val) {
        console.log('dialogVisibles属性发生了变化', val)
        this.dialogStatus = val
      },
      // 监听副本属性
      // 组件内对dialogStatus变更后向外部发送事件通知
      dialogStatus (val) {
        console.log('向外发送事件通知..', val)
        this.$emit('changeVisible', val)
      }
    },
    methods: {
      handleClose (done) {
        this.$confirm('确认关闭?')
          .then(_ => {
            done()
          })
          .catch(_ => {})
      }
    }
  }
</script>

刚开始渲染:

父组件点击创建方法触发

子组件属性发生变化,同时通过事件通知给了父组件

弹出框点击关闭之后:

同理父子组件属性也同步变化了。