深刻理解Vue父子組件通信的屬性和事件

  在html中使用元素,會有一些屬性,如class,id,還能夠綁定事件,自定義組件也是能夠的。當在一個組件中,使用了其餘自定義組件時,就會利用子組件的屬性事件來和父組件進行數據交流。html

  父子組件之間的通訊就是 props down,events up,父組件經過 屬性props向下傳遞數據給子組件,子組件經過 事件events 給父組件發送消息數組

  好比,子組件須要某個數據,就在內部定義一個prop屬性,而後父組件就像給html元素指定特性值同樣,把本身的data屬性傳遞給子組件的這個屬性。而當子組件內部發生了什麼事情的時候,就經過自定義事件來把這個事情涉及到的數據暴露出來,供父組件處理。緩存

<my-component v-bind:foo="baz" v-on:event-a="doThis(arg1,...arg2)"></my-component>

  如上代碼:app

    foo<my-component>組件內部定義的一個prop屬性,baz是父組件的一個data屬性,函數

    event-a是子組件定義的一個事件,doThis是父組件的一個方法post

  過程就是這樣:ui

    父組件把baz數據經過prop傳遞給子組件的foothis

    子組件內部獲得foo的值,就能夠進行相應的操做;spa

    當子組件內部發生了一些變化,但願父組件能知道時,就利用代碼觸發event-a事件,把一些數據發送出去;code

    父組件把這個事件處理器綁定爲doThis方法,子組件發送的數據,就做爲doThis方法的參數被傳進來;

    而後父組件就能夠根據這些數據,進行相應的操做。

一、屬性Props

  Vue組件經過props屬性來聲明一個本身的屬性,而後父組件就能夠往裏面傳遞數據。

Vue.component('mycomponent',{ template: '<div>這是一個自定義組件,父組件傳給個人內容是:{{myMessage}}</div>', props: ['myMessage'], data () { return { message: 'hello world' } } })

  而後調用該組件

<div id="app">
    <mycomponent :my-message="hello"></mycomponent>
</div>

  注意,因爲HTML特性是不區分大小寫的,因此傳遞屬性值時,myMessage應該轉換成 kebab-case (短橫線隔開式)my-message="hello"

二、v-bind綁定屬性值

  這裏說一下v-bind綁定屬性值的一個特性:通常狀況下,使用v-bind給元素特性(attribute)傳遞值時,Vue會將""中的內容當作一個表達式。好比:

<div attr="message">hello</div>
//上面這樣,div元素的attr特性值就是message。 //而這樣
<div v-bind:attr="message">hello</div>
//這裏的message應該是Vue實例的data的一個屬性,這樣div元素的attr特性值就是message這個屬性的值。

  之因此說是通常狀況,是由於classstyle特性並非這樣。用v-bind:classclass傳入正常的類名,效果是同樣的,由於對於這兩個特性,Vue採用了合併而不是替換的原則。

三、子組件但願對傳入的prop進行操做

  通常來講,是不建議在子組件中對父組件中傳遞來的屬性進行操做的。若是真的有這種需求,能夠這樣:

  父組件傳遞了一個基本類型值,那麼能夠在子組件中建立一個新的屬性,並以傳遞進來的值進行初始化,以後就能夠操做這個新的屬性

props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }

  父組件傳遞了一個引用類型值,爲了不更改父組件中相應的數據,最好是對引用類型進行復制。複雜的狀況,確定應該是深複製

四、給子組件傳遞正確類型的值

  一樣是上面的緣由,靜態的給子組件的特性傳遞值,它都會把他當作一個字符串。

<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>

  子組件中,特性的值是字符串 "1" 而不是 number 1。若是想傳遞正確的數值,應該使用v-bind傳遞,這樣就能把傳遞的值當作一個表達式來處理,而不是字符串。

<!-- 傳遞實際的 number 1 -->
<comp v-bind:some-prop="1"></comp>

五、Prop驗證

  咱們能夠給組件的props屬性添加驗證,當傳入的數據不符合要求時,Vue會發出警告。

Vue.component('example', { props: { // 基礎類型檢測 (`null` 意思是任何類型均可以)
 propA: Number, // 多種類型
 propB: [String, Number], // 必傳且是字符串
 propC: { type: String, required: true }, // 數字,有默認值
 propD: { type: Number, default: 100 }, // 數組/對象的默認值應當由一個工廠函數返回
 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定義驗證函數
 propF: { validator: function (value) { return value > 10 } } } })

  type 能夠是下面原生構造器:String、Number、Boolean、Function、Object、Array、Symbol

  type 也能夠是一個自定義構造器函數,使用 instanceof 檢測。

// 自定義Person構造器
 function Person(name, age) { this.name = name this.age = age } Vue.component('my-component', { template: `<div>名字: {{ person-prop.name }}, 年齡: {{ person-prop.age }} </div>`, props: { person-prop: { type: Person // 指定類型
 } } }) new Vue({ el: '#app2', data: { person: 2        // 傳入Number類型會報錯
 } })

六、非Prop類型的屬性

  也能夠像在html標籤中添加data-開頭的自定義屬性同樣,給自定義組件添加任意的屬性,而不只限於data-*形式,這樣作的話,Vue會把這個屬性放在自定義組件的根元素上。一個自定義組件的模板只能有一個根元素

  覆蓋非Prop屬性:若是父組件向子組件的非prop屬性傳遞了值,那麼這個值會覆蓋子組件模板中的特性

<div id="app3">
    <my-component2 att="helloParent"></my-component2>
</div>
<script> Vue.component('my-component2', { template: `<div att="helloChild">子組件原有的特性被覆蓋了</div>` }) new Vue({ el: '#app3' }) </script>

  上面渲染的結果是,divatt屬性是helloParent

  注意:前面已經提到過,覆蓋原則對於classstyle不適用,而是採用了合併(merge)的原則

<div id="app3">
    <my-component2 att="helloParent" class="class2" style="color: red;"></my-component2>
</div>
<script> Vue.component('my-component2', { template: `<div att="helloChild" class="class1" style="background: yellow;">子組件原有的特性被覆蓋了</div>` }) new Vue({ el: '#app3' }) </script>

  上面的渲染結果是,div的類名是class1 class2,行內樣式是color:red; background:yellow;

七、自定義事件

  經過prop屬性,父組件能夠向子組件傳遞數據,而子組件的自定義事件就是用來將內部的數據報告給父組件的。

<div id="app3">
    <my-component2 v-on:myclick="onClick"></my-component2>
</div>
<script> Vue.component('my-component2', { template: `<div>
    <button type="button" @click="childClick">點擊我觸發自定義事件</button>
    </div>`, methods: { childClick () { this.$emit('myclick', '這是我暴露出去的數據', '這是我暴露出去的數據2') } } }) new Vue({ el: '#app3', methods: { onClick () { console.log(arguments) } } }) </script>

  如上所示,共分爲如下步驟:

  (1)子組件在本身的方法中將自定義事件以及須要發出的數據經過如下代碼發送出去

this.$emit('myclick', '這是我暴露出去的數據', '這是我暴露出去的數據2')
  • 第一個參數是自定義事件的名字

  • 後面的參數是依次想要發送出去的數據

  (2)父組件利用v-on爲事件綁定處理器

<my-component2 v-on:myclick="onClick"></my-component2>

  這樣,在Vue實例的methods方法中就能夠調用傳進來的參數了

  注意在使用v-on綁定事件處理方法時,不該該傳進任何參數,而是直接寫v-on:myclick="onClick",否則,子組件暴露出來的數據就沒法獲取到了

八、動態組件

  經過使用保留的 <component> 元素,動態地綁定到它的 is 特性,可讓多個組件使用同一個掛載點,並動態切換:

<div id="app6">
    <select v-model="currentComponent">
      <option value="home">home</option>
      <option value="post">post</option>
      <option value="about">about</option>
    </select>
    <component :is="currentComponent"></component>
  </div>
  <script>
      new Vue({ el: '#app6', data: { currentComponent: 'home' }, components: { home: { template: `<header>這是home組件</header>` }, post: { template: `<header>這是post組件</header>` }, about: { template: `<header>這是about組件</header>` } } }) </script>

  也能夠直接綁定到組件對象上:

var Home = { template: `<header>這是home組件</header>` } new Vue({ el: '#app6', data: { currentComponent: Home } })

九、保留切換出去的組件,避免從新渲染

  若是把切換出去的組件保留在內存中,能夠保留它的狀態或避免從新渲染。爲此能夠添加一個 keep-alive 指令參數:

<keep-alive>
  <component :is="currentComponent">
    <!-- 非活動組件將被緩存! -->
  </component>
</keep-alive>
相關文章
相關標籤/搜索