vue學習記錄⑤(組件通訊-父與子)

今天咱們看一下組件通訊。html

通過前面幾篇文章,咱們已經能夠構建出完整的單個組件,並利用路由使其串聯起來訪問了。vue

但這明顯仍是不夠的。一個頁面不可能就是個單組件,通常是由多個組件合成的。正由於如此,組件之間確定是有相互關係的,咱們就稱這種現象叫組件通訊ajax

 

好比父組件發生了某項改變,子組件會跟着相應發生變化;反過來,子組件有了某種改變,父組件有時也會隨之作出調整。那麼這種現象咱們稱之爲雙向數據流動vue-router

然而,vue的做者敏銳的認識到,雙向數據流帶來便捷的同時,也存在着極大的安全隱患。npm

 

父組件將變化傳遞給子組件(父影響子),這是沒問題的,在咱們平常生活中也是極爲廣泛的現象(老子教訓兒子是天經地義的),然而反過來子影響父的話,這就變得不可理喻了(兒子教訓老子?)json

子組件修改父組件,增大了組件之間的耦合度。有時開發者根本沒有意識到這種修改,這猶如埋下一顆定時炸彈,隨着往後項目代碼的膨脹,一旦引爆,問題排查難度也會呈指數級的徒增。數組

爲了斬斷這種安全隱患,vue提倡的是單向數據流動——也就是隻能父影響子,而反過來則不成立。安全

 

父傳子,咱們利用props做爲橋樑。下面看一個例子。異步

首先我將目錄調整一下:函數

其中,helloworld.vue組件,咱們設定其爲父組件;而children目錄下的child.vue,咱們設定爲其子組件。

Helloworld.vue代碼以下:

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父親</h1>
<!--綁定父組件的某個變量myMsg-->
  <div>
<!--注意子組件接受變量時,須要變爲駝峯命令法sendMessage-->
   <child v-bind:send-message="myMsg"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子組件,須要設定子組件的name爲child
import child from './children/child.vue'
export default {
 name:'helloworld',
 data(){
  return {
   myMsg:'hello,my son!'
  };
 },
 components:{child}//局部註冊子組件
}
</script>
<!--樣式部分-->
<style>
.container{
 background: #ccc;
 color:greenyellow;
}

</style>

你們注意下別把組件和路由的概念搞混了,兩者之間一毛錢關係都沒有。要使用組件,需作局部或者全局註冊。我這裏只作局部註冊,你們仔細看一下寫法。

咱們在父組件定義了一個myMsg這個變量,而後經過v-bind綁定到了child組件中。這時,child組件就能夠經過props來接收這個變量了。

child.vue代碼以下:

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是兒子</h3>

  <div>我從父親那邊接收過來的信息:</div>
  <div>{{sendMessage}}</div>
 </div>
</template>
<!--js部分-->
<script>
//子組件
export default {
 name:'child',//必須設定name,不然沒法在父組件import
 props:['sendMessage'],
 data(){
  return {

  };
 }
}
</script>
<!--樣式部分-->
<style>
.sub-container{
 background: blue;
 color:red;
}
</style>

一個簡單的父子通訊案例就完成了,看一下路由router/index.js,代碼以下:

import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/Hello'
import HelloWorld from '@/components/Helloworld' //咱們新定義的組件

Vue.use(Router)

export default new Router({
  routes: [{
      path: '/',
      name: 'Hello',
      component: Hello
    },
    { //新路由
      path: '/helloworld/:id',
      name: 'HelloWorld',
      component: HelloWorld,
    }
  ]
})

運行一下npm run dev,看一下結果:

能夠看到,子組件順利接收到了父組件傳來的信息。

那麼,到底子組件可否影響父組件呢?咱們將以上代碼作一下調整。

調整後的Helloworld.vue

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父親</h1>

  <input type="text" v-model="myMsg">
  <br>
<!--綁定父組件的某個變量myMsg-->
  <div>
<!--注意子組件接受變量時,須要變爲駝峯命令法sendMessage-->
   <child v-bind:send-message="myMsg"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子組件,須要設定子組件的name爲child
import child from './children/child.vue'
export default {
 name:'helloworld',
 data(){
  return {
   myMsg:'hello,my son!'
  };
 },
 components:{child}//局部註冊子組件
}
</script>
<!--樣式部分-->
<style>
.container{
background: #ccc;
color:greenyellow;
}

</style>

調整後的child.vue

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是兒子</h3>

  <div>我從父親那邊接收過來的信息:</div>
  <div>{{sendMessage}}</div>
  <div>是否能夠子影響父呢?</div>
  <input type="text" v-model="sendMessage">
 </div>
</template>
<!--js部分-->
<script>
//子組件
export default {
 name:'child',//必須設定name,不然沒法在父組件import
 props:['sendMessage'],
 data(){
  return {

  };
 }
}
</script>
<!--樣式部分-->
<style>
.sub-container{
 background: blue;
 color:red;
}
</style>

運行一下npm run dev,看一下結果:

能夠看出,子組件是沒法影響父組件的,這樣父組件就成功解耦了。

你們要注意一個問題——傳遞的數據類型問題。

傳遞的是值類型確定沒問題,假如是對象或者數組這樣的引用類型,變量共用同一內存,因此父子會出現雙向影響,這個問題必定要注意。

爲了杜絕隱患,提升安全性,咱們在子組件將prop接收的類型作一下強制檢測,假如傳入的數據類型並非我須要的,能夠拋出異常。

Helloworld.vue

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父親</h1>

  <input type="text" v-model="myMsg">
<!--<input type="text" v-model="myNum">-->
  <ul v-for="item of myObj">
   <li>{{item.name}}</li>
  </ul>
 <br>
<!--綁定父組件的某個變量myMsg-->
  <div>
<!--注意子組件接受變量時,須要變爲駝峯命令法sendMessage-->
   <child :send-message="myMsg" :send-num="myNum" :send-obj="myObj"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子組件,須要設定子組件的name爲child
import child from './children/child.vue'
export default {
 name:'helloworld',
 data(){
  return {
   myMsg:'hello,my son!',
   myNum:'123',
   myObj:[{id:1,name:'Tom_Lo'},{id:2,name:'tom'}]
  };
 },
 components:{child}//局部註冊子組件
}
</script>
<!--樣式部分-->
<style>
.container{
 background: #ccc;
 color:green;
}
</style>

父組件此次傳了三個不一樣類型的信息,分別是字符串、數字和json對象。其中,數字類型我故意寫錯了,看一會兒組件是否會檢測出來。

child.vue

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是兒子</h3>

  <div>我從父親那邊接收過來的信息:</div>
  <div>字符串:{{sendMessage}}</div>
  <div>數字:{{sendNum}}</div>
  <div>
   <ul v-for="item of list">
    <li>{{item.name}}</li>
   </ul>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//子組件
export default {
 name:'child',//必須設定name,不然沒法在父組件import
 props:{
 sendMessage:{
  type:String,//傳入類型必須是字符串
  required:true//必傳
 },
 sendNum:{
  type:Number,//傳入類型必須是數字
  required:true
 },
//若是是數組或對象
 sendObj:{
  validator:function(val){

   if(Object.prototype.toString.call(val) === '[object Array]' || Object.prototype.toString.call(val) === '[object Object]'){
    return true;
    }
   }
  }
 },//聲明驗證類型
 data(){
  var parentObj = this.sendObj;//獲取父組件傳來的json
  var childObj =JSON.parse(JSON.stringify(parentObj));//複製一份,避免污染父組件的數據
  childObj.push({id:3,name:'mike'});//追加一個對象
   return {
    list:childObj
   };
 }
}
</script>
<!--樣式部分-->
<style>
.sub-container{
background: blue;
color:red;
}
</style>

你們須要注意下子組件props的驗證規則。

那麼對於容易引起錯誤的引用類型,你們應該如何避免呢?

當咱們接收過來這個對象數據後,先將其拷貝一份副本,而後用副本作增長修改等操做,這樣便不會影響到父組件的數據了。

 

咱們運行一下看看:

看起來驗證規則生效了哈~~

那麼以上都是同步的狀況,那麼異步呢?

假設父組件的數據是異步ajax獲取的,而後渲染頁面。那麼此時,子組件如何跟着更新視圖呢?

這就得有個監聽機制。當子組件監聽到父組件的數據到位後,將本地數據更新便可。

vue提供的watch能夠達到此目的,咱們看一下例子。

Helloworld.vue

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父親</h1>
  <input type="text" v-model="myMsg">
<!--<input type="text" v-model="myNum">-->
  <ul v-for="item of myObj">
   <li>{{item.name}}</li>
  </ul>
 <br>
<!--綁定父組件的某個變量myMsg-->
  <div>
<!--注意子組件接受變量時,須要變爲駝峯命令法sendMessage-->
   <child :send-message="myMsg" :send-num="myNum" :send-obj="myObj"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子組件,須要設定子組件的name爲child
import child from './children/child.vue'
export default {
 name:'helloworld',
 data(){
//初始化obj
  var obj = {
   myMsg:'hello,my son!',
   myNum:123,
   myObj:[]
  };
//延時返回myObj,模擬ajax的延時狀況
  setTimeout(function(){
   obj['myObj'] = [{id:1,name:'jack'},{id:2,name:'tom'}];

  },2000);
  return obj;
 },
 components:{child}//局部註冊子組件
}
</script>
<!--樣式部分-->
<style>
.container{
 background: #ccc;
 color:green;
}
</style>

child.vue

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是兒子</h3>

  <div>我從父親那邊接收過來的信息:</div>
  <div>字符串:{{sendMessage}}</div>
  <div>數字:{{sendNum}}</div>
  <div>
   <ul v-for="item of list">
     <li>{{item.name}}</li>
   </ul>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//子組件
export default {
 name:'child',//必須設定name,不然沒法在父組件import
 props:{
  sendMessage:{
   type:String,//傳入類型必須是字符串
   required:true//必傳
  },
  sendNum:{
   type:Number,//傳入類型必須是數字
   required:true
  },
//若是是數組或對象
  sendObj:{
   validator:function(val){

    if(Object.prototype.toString.call(val) === '[object Array]' || Object.prototype.toString.call(val) === '[object Object]'){
     return true;
    }
   }
  }
 },//聲明驗證類型
 data(){

   return {
    list:null
   };
 },
 watch:{//監控父組件sendObj的變化
  sendObj(newval,oldval){

   if(newval !== oldval){

    var newobj =JSON.parse(JSON.stringify(newval));//複製一份,避免污染父組件的數據
    newobj.push({id:3,name:'mike'});//追加一個對象
    this.list = newobj;//將新值賦予data的list
   }

  }
 }
}
</script>
<!--樣式部分-->
<style>
.sub-container{
 background: blue;
 color:red;
}
</style>

watch的函數名稱,就是要監聽的值,這裏咱們監聽父組件傳來的prop;而newval和oldval參數,分別表明了新值和舊值。

在父組件未更新以前,子組件監聽到的sendObj,新值和舊值都是空數組,兩者是相等的;而當監聽到兩者發生不等時,就說明父組件傳來的信息發生了改變。咱們取新值,並將拷貝後的數據傳給data裏面的list,而後更新到視圖。

看一下運行狀況:

父組件與子組件的交互就實現了。

參考文章:http://mp.weixin.qq.com/s/rrKGRPTIt-aiZXijIli3ww

參考文章:http://www.cnblogs.com/ghostwu/p/7518002.html

相關文章
相關標籤/搜索