父子組件之間的通訊就是 props down,events up,父組件經過 屬性props向下傳遞數據給子組件,子組件經過 事件events 給父組件發送消息。
好比,子組件須要某個數據,就在內部定義一個prop屬性,而後父組件就像給html元素指定特性值同樣,把本身的data屬性傳遞給子組件的這個屬性。
而當子組件內部發生了什麼事情的時候,就經過自定義事件來把這個事情涉及到的數據暴露出來,供父組件處理。html
<my-component v-bind:foo="baz" v-on:event-a="doThis(arg1,...arg2)"></my-component>
如上代碼,vue
過程就是這樣:npm
Vue組件經過props屬性來聲明一個本身的屬性,而後父組件就能夠往裏面傳遞數據。數組
Vue.component('mycomponent',{ template: '<div>這是一個自定義組件,父組件傳給個人內容是:{{myMessage}}</div>', props: ['myMessage'], data () { return { message: 'hello world' } } })
而後調用該組件app
注意,因爲HTML特性是不區分大小寫的,因此傳遞屬性值時,myMessage應該轉換成 kebab-case (短橫線隔開式)my-message="hello"。函數
<div id="app"> <mycomponent my-message="hello"></mycomponent> </div>
v-bind綁定屬性值的一個特性:通常狀況下,使用v-bind給元素特性(attribute)傳遞值時,Vue會將""中的內容當作一個表達式。ui
//好比 <div attr="message">hello</div>
上面這樣,div元素的attr特性值就是message。this
<div v-bind:attr="message">hello</div>
這裏的message應該是Vue實例的data的一個屬性,這樣div元素的attr特性值就是message這個屬性的值。.net
之因此說是通常狀況,是由於class和style特性並非這樣。用v-bind:class和class傳入正常的類名,效果是同樣的,由於對於這兩個特性,Vue採用了合併而不是替換的原則。code
根據上面,想要把父組件的屬性綁定到子組件,應該使用v-bind,這樣,父組件中數據改變時能反映到子組件。
注意,根據父組件傳遞給子組件的屬性類型的不一樣,當在子組件中更改這個屬性時,會有如下兩種狀況:
<div id="app"> <div>這是父組件的parentArray:{{parentArray}}</div> <my-component :child-array="parentArray"></my-component> </div> <script> Vue.component('my-component', { template: ` <div>這是接收了父組件傳遞值的子組件的childArray: {{childArray}} <br> <button type="button" @click="changeArray"> 點擊我改變父元素的parentArray</button> </div>`, props: ['childArray'], data () { return { counter: 1 } }, methods: { changeArray () { this.childArray.push(this.counter++) } } }) new Vue({ el: '#app', data: { parentArray: [] } }) </script>
點擊結果
當父組件傳遞值爲基本類型時,在子組件中更改這個屬性會報錯。正確的作法是,在父組件中綁定屬性值時,加上.sync修飾符。
<div id="app2"> <div>這是父組件的parentArray:{{parent}}</div> <my-component-sync :child.sync="parent"></my-component-sync> </div> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component('myComponentSync', { template: `<div>這是接收了父組件傳遞值的子組件的child: {{child}} <br> <button type="button" @click="change"> 點擊我改變父元素的parent</button> </div>`, props: ['child'], data() { return { counter: this.child } }, methods: { change() { this.counter++ this.$emit('update:child', this.counter) } } }) new Vue({ el: '#app2', data: { parent: 0 } }) </script>
點擊結果以下圖,去掉沒法改變
通常來講,是不建議在子組件中對父組件中傳遞來的屬性進行操做的。若是真的有這種需求,能夠這樣:
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>
咱們能夠給組件的props屬性添加驗證,當傳入的數據不符合要求時,Vue會發出警告。
Vue.component('myComponent', { 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 能夠是下面原生構造器:
// 自定義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類型會報錯 } })
也能夠像在html標籤中添加data-開頭的自定義屬性同樣,給自定義組件添加任意的屬性。而不只限於data-*形式,這樣作的話,Vue會把這個屬性放在自定義組件的根元素上。一個自定義組件的模板只能有一個根元素。
若是父組件向子組件的非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>
上面渲染的結果是,div的att屬性是helloParent。
注意:前面已經提到過,覆蓋原則對於class和style不適用,而是採用了合併(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;。