vue2元件之間雙向資料繫結問題

sumer7310發表於2017-10-30

最近在使用element-ui的dialog元件二次封裝成獨立元件使用時,子元件需要將關閉dialog狀態返回給父元件,簡單的說就是要實現父子元件之間的資料雙向繫結問題。

大致程式碼如下:

1,父元件

<template>
  <button @click="openDialog">開啟彈窗</button>
  <dialogCompenent :show="result" :result="result" @dialogData="closeDialog"></dialogCompenent>
</template>
<script type="text/babel">
import dialogCompenent from '/dialogCompenent'
export default {
  data () {
    return {
      result: false
    }
  },
  components: {
    dialogCompenent
  },
  methods: {
    openDialog () {
      this.result = true
    },
    closeDialog (data) {
      this.result = data
    }
  }
}
</script>

2,子元件 childCompenent

<template>
<el-dialog
  title="彈框元件"
  :visible.sync="result"
  @open="doOpen"
  @close="doClose"
  :show="result"
  size="tiny">
  <div class="content-wrapper">
    具體業務程式碼...
  </div>
</el-dialog>
  </div>
</template>
<script type="text/babel">
  import videoApi from '../../api/videoApi/videoApi'
  export default {
    name: 'dialogCompenent',
    props: {
      result: Boolean
    },
    methods: {
      doOpen () {
        ...
      },
      doClose () {
        this.$emit('dialogData', false)
        ...
      }
    }
  }
</script>

程式在瀏覽器上雖然能夠正常執行,但是在vue2.0卻會報錯:

[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 )

元件內不能修改props的值,同時修改的值也不會同步到元件外層,即呼叫元件方不知道元件內部當前的狀態是什麼

這是什麼原因造成的呢?

在vue1.x版本中利用props的twoWay和.sync繫結修飾符就可以實現props的雙向資料繫結。

在vue2.0中移除了元件的props的雙向資料繫結功能,如果需要雙向繫結需要自己來實現。
在vue2.0中元件的props的資料流動改為了只能單向流動,即只能由(父元件)通過元件的v-bind:attributes傳遞給(子元件),子元件只能被動接收父元件傳遞過來的資料,並在子元件內不能修改由父元件傳遞過來的props資料。

官方文件解釋:

prop 是單向繫結的:當父元件的屬性變化時,將傳導給子元件,但是不會反過來。這是為了防止子元件無意修改了父元件的狀態——這會讓應用的資料流難以理解。

雖然廢棄了props的雙向繫結對於整個專案整體而言是有利且正確的,但是在某些時候我們確實需要從元件內部修改props的需求

在Vue2.0中,實現元件屬性的雙向繫結方式

子元件修改:

<template>
    <el-dialog
      title="彈框元件"
      :visible.sync="openStatus"
      @open="doOpen"
      @close="doClose"
      :show="openStatus"
      size="tiny">
      <div class="content-wrapper">
        具體業務程式碼...
      </div>
    </el-dialog>
  </div>
</template>
<script type="text/babel">
  import videoApi from '../../api/videoApi/videoApi'
  export default {
    name: 'dialogCompenent',
    props: {
      result: Boolean
    },
    /*建立一個openStatus變數快取result資料
    *在子元件需要呼叫result的地方呼叫data物件openStatus
    */
    data () {
      return {
        openStatus: this.result
      }
    },
    //新增result的watch,監聽變更同步到openStatus
    //監聽父元件對props屬性result的修改,並同步到元件內的data屬性
    watch: {
      result (val) {
        this.openStatus = val
      }
    },
    methods: {
      doOpen () {
        ...
      },
      doClose () {
        this.$emit('dialogData', false)//子元件對openStatus修改後向父元件傳送事件通知
        ...
      }
    }
  }
</script>

父元件修改:

<template>
  <button @click="openDialog">開啟彈窗</button>
  <dialogCompenent :show="result" :result="result" @dialogData="closeDialog"></dialogCompenent>
</template>
<script type="text/babel">
import dialogCompenent from '/dialogCompenent'
export default {
  data () {
    return {
      result: false
    }
  },
  components: {
    dialogCompenent
  },
  methods: {
    openDialog () {
      this.result = true
    },
    closeDialog (data) {
      this.result = data//子元件觸發父元件事件,進行資料變更,同步result資料
    }
  }
}
</script>

至此,實現了子元件內資料與父元件的資料的雙向繫結,元件內外資料的同步。最後歸結為一句話就是:元件內部自己變了告訴外部,外部決定要不要變。

結語
那麼為什麼vue1.0還有的資料雙向繫結在vue2.0版本中反而拋棄了呢,通過上述案例我們也可以發現雙向繫結的props程式碼多,不利於元件間的資料狀態管理,尤其是在複雜的業務中更是如此,所以儘量不使用這種方式的雙向繫結,過於複雜的資料處理使用vuex來進行資料管理。(https://vuex.vuejs.org/zh-cn/intro.html)

相關文章