點燈坊

失くすものさえない今が強くなるチャンスよ

使 Prop 也能 Reactive

Sam Xiao's Avatar 2020-10-26

若 Prop 只用來對 Component 傳入初始值設定,只要在 mounted hook 接受並寫入 data 即可;但若日後還會將改變 Data 傳入 Prop,且希望 Component 能 Reactive 根據 Prop 改變,則要搭配 Computed 或 Watch。

Version

Vue 2.6.11

Computed

prop000

+ 不斷遞增 component 外部的 state,並以 prop 傳入 component 並 reactive 顯示。

App.vue

<template>
  <div>
    <my-counter :start-count="count"/>
    <button @click="onClick">+</button>
  </div>
</template>

<script>
import MyCounter from '@/components/MyCounter'

let onClick = function() {
  this.count++
}

export default {
  components: {
    MyCounter
  },
  data: () => ({
    count: 5
  }),
  methods: {
    onClick
  }
}
</script>

19 行

data: () => ({
  count: 5
}),

count state 初始值設定為 5

第 3 行

<my-counter :start-count="count"/>

count state 傳入 start-count prop。

11 行

let onClick = function() {
  this.count++
}

在 component 外對 count state 遞增,期望 state 改變,連帶著傳進 prop 也能 reactive 改變。

MyCounter.vue

<template>
  <div>{{ count }}</div>
</template>

<script>
let count = ({ startCount }) => startCount

export default {
  props: {
    startCount: { default: 0 }
  },
  computed: {
    count
  }
}
</script>

第 9 行

props: {
  startCount: { default: 0 }
},

宣告 startCount prop,且預設值為 0,也代表其 type 為 Number。

第 6 行

let count = ({ startCount }) => startCount

可從 argument 直接 destructure 出 startCount prop 回傳產生 count computed。

為了讓 prop 能 reactive,最簡單的方式是使用 computed

Watch

App.vue

<template>
  <div>
    <my-counter :start-count="count"/>
    <button @click="onClick">+</button>
  </div>
</template>

<script>
import MyCounter from '@/components/MyCounter'

let onClick = function() {
  this.count++
}

export default {
  components: {
    MyCounter
  },
  data: () => ({
    count: 5
  }),
  methods: {
    onClick
  }
}
</script>

App.vue 部分不變。

MyCounter.vue

<template>
  <div>{{ count }}</div>
</template>

<script>
let startCount_ = function(v) {
  this.count = v
}

export default {
  props: {
    startCount: { default: 0 }
  },
  data: ({ startCount }) => ({
    count: startCount
  }),
  watch: {
    startCount: startCount_
  }
}
</script>

直接使用 computed 接 prop 雖然可行,但其缺點是不能有 data

14 行

data: ({ startCount }) => ({
  count: startCount
}),

直接由 argument 解構 startCount prop,由 prop 初始值定義 count data。

17 行

watch: {
  startCount: startCount_
}

定義 startCount prop 的 watch,一旦日後 prop 改變,將觸發 startCount_ handler 處理。

第 6 行

let startCount_ = function(v) {
  this.count = v
}

一旦 startCount prop 改變,將其值立即修改 count data。

若要使用 data,則要先將 prop 初始值給 data,再對 prop 進行 watch

immediate: true

App.vue

<template>
  <div>
    <MyCounter :start-count="count"/>
    <button @click="onClick">+</button>
  </div>
</template>

<script>
import MyCounter from '@/components/MyCounter'

let onClick = function() {
  this.count++
}

export default {
  components: {
    MyCounter
  },
  data: () => ({
    count: 5
  }),
  methods: {
    onClick
  }
}
</script>

App.vue 部分不變。

MyCounter.vue

<template>
  <div>{{ count }}</div>
</template>

<script>
let startCount_ = function(v) {
  this.count = v
}

export default {
  props: {
    startCount: { default: 0 }
  },
  data: () => ({
    count: 0
  }),
  watch: {
    startCount: {
      handler: startCount_,
      immediate: true
    }
  }
}
</script>

17 行

watch: {
  startCount: {
    handler: startCount_,
    immediate: true
  }
}

若覺得要先由 prop 定義 data 初始值麻煩,也可以在 watch 直接定義 immediatetrue,且對 handler property 定義 startCount_ handler,如此會一併定義 data 初始值。

Conclusion

  • 若要 prop 能 reactive,最簡單方式是使用 computed,唯沒有 data 可用
  • 若要有 data 也能 reactive, 則要對 prop 使用 watch,先以 prop 定義 data 初始值,再由 watch 將日後改變的 prop 修改 data
  • 若想一次由 prop 定義 data 初始值,也可直接在 watch 內定義 immediate: true