Vue父子組件經過prop異步傳輸數據踩坑

今天碰到vue開發父子組件通訊的一個小坑,狀況是這樣的:子組件使用echart展現圖表,所需options由父組件經過prop傳入,父組件中的options初始值爲空,在mounted鉤子函數中發起http請求獲取數據而後更新options,結果子組件沒法正確顯示圖表,通過一番查找解決了問題,經過本文作個記錄,也但願能幫到之後遇到相同問題的小夥伴,示例採用demo演示vue

父組件

<template>
  <div id="app">
    <child :message="message"></child>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: {child },
  data() {
    return {
      message:{}
    };
  },
  mounted(){
    // 模擬異步請求
    setTimeout( () => this.message.age = 18,2000)
  }
};
</script>
複製代碼

子組件

<template>
    <div>{{age}}</div>
</template>

<script type='text/ecmascript-6'>
export default {
  props: ["message"],
  mounted(){
  // 模擬echart的初始化操做
    this.age = this.message.age
  },
  data() {
    return {
      age: null
    };
  }
};
</script>
複製代碼

實際效果如圖

其實這裏子組件是拿到了更新後的值,若是template中的是 {{message.age}}是能夠顯示出18的,可是項目中是傳的options,子組件有個echart的setOptions操做,並非直接將prop的數據展現,因此demo寫成這樣。這裏是能夠分析出問題所在的,子組件mounted時父組件傳來的值 message爲空對象, this.message.age就是underfined,因此頁面顯示爲空,2秒後父組件的值更新,子組件能夠拿到新的值,但mounted鉤子函數再也不觸發,因此 age仍爲underfined,在網上查了下,一般的解決辦法是使用 watch來監聽prop。改進後代碼以下

子組件

<template>
    <div>{{age}}</div>
</template>

<script type='text/ecmascript-6'>
export default {
  props: ["message"],
  mounted() {
    this.age = this.message.age;
  },
  watch: {
      message(nv,ov){
          this.age = nv.age
      }
  },
  data() {
    return {
      age: null
    };
  }
};
</script>
複製代碼

看到網上的教程基本到這就結束了,子組件均可以正確渲染,but...我這頁面仍是老樣子,依舊一片空白,查看下調試工具,仍是這樣:bash

我擦,這是什麼鬼,debug一下發現watch裏的代碼根本不會執行,可message明明變了啊,因而又在網絡中遨遊了一番,而後看到了這麼一段話。 網絡

Vue在實例化的時候會給data中的每一個屬性加上 getter setter以實現響應式更新,可是新增的屬性沒法實現響應式更新,這裏咱們的父組件中message初始值爲 {},2秒中後設置他的age爲18,由於age是新增的屬性,實例初始化的時候並無給age加上getter和setter因此watch失敗。這裏盜一張官網的圖

修改代碼,在父組件中message的初始值中添加age

<template>
  <div id="app">
    <child :message="message"></child>
  </div>
</template>

<script>
import child from "./components/child";
export default {
  name: "app",
  components: {child },
 
  data() {
    return {
      message:{age:null}
    };
  },
  mounted(){
    // 模擬異步請求
    setTimeout( () => this.message.age = 18,2000)
  }
};
</script>
複製代碼

嗯,應該沒問題了吧,打開頁面,額。。。仍是熟悉的頁面app

有點小崩潰,冷靜,再分析一波。子組件watch了message,可是咱們在異步代碼中執行了 this.message.age = 18而不是相似 this.message = xxx這種操做,雖然message這個對象確實發生了改變,可是卻沒法觸發setter,watch也就不起做用,查了下官網,發現了這個配置:

修改代碼以下:

<template>
    <div>{{age}}</div>
</template>

<script type='text/ecmascript-6'>
export default {
  props: ["message"],
  mounted() {
    this.age = this.message.age;
  },
  watch: {
    message: {
      deep: true,
      handler(nv, ov) {
        this.age = nv.age;
      }
    }
  },
  data() {
    return {
      age: null
    };
  }
};
</script>
複製代碼

再次打開頁面ecmascript

ok,完結撒花!!!
相關文章
相關標籤/搜索