在html中使用元素,會有一些屬性,如class,id,還可以綁定事件,自定義組件也是可以的。當在一個組件中,使用了其他自定義組件時,就會利用子組件的屬性和事件來和父組件進行數據交流。
父子組件之間的通信就是 props down,events up,父組件通過 屬性props向下傳遞數據給子組件,子組件通過 事件events 給父組件發送消息。
比如,子組件需要某個數據,就在內部定義一個prop屬性,然后父組件就像給html元素指定特性值一樣,把自己的data屬性傳遞給子組件的這個屬性。而當子組件內部發生了什么事情的時候,就通過自定義事件來把這個事情涉及到的數據暴露出來,供父組件處理。
<my-component v-bind:foo="baz" v-on:event-a="doThis(arg1,...arg2)"></my-component>
如上代碼:
foo是<my-component>組件內部定義的一個prop屬性,baz是父組件的一個data屬性,
event-a是子組件定義的一個事件,doThis是父組件的一個方法
過程就是這樣:
父組件把baz數據通過prop傳遞給子組件的foo;
子組件內部得到foo的值,就可以進行相應的操作;
當子組件內部發生了一些變化,希望父組件能知道時,就利用代碼觸發event-a事件,把一些數據發送出去;
父組件把這個事件處理器綁定為doThis方法,子組件發送的數據,就作為doThis方法的參數被傳進來;
然后父組件就可以根據這些數據,進行相應的操作。
1、屬性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"。
2、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這個屬性的值。
之所以說是一般情況,是因為class和style特性並不是這樣。用v-bind:class和class傳入正常的類名,效果是一樣的,因為對於這兩個特性,Vue采用了合並而不是替換的原則。
3、子組件希望對傳入的prop進行操作
一般來說,是不建議在子組件中對父組件中傳遞來的屬性進行操作的。如果真的有這種需求,可以這樣:
父組件傳遞了一個基本類型值,那么可以在子組件中創建一個新的屬性,並以傳遞進來的值進行初始化,之后就可以操作這個新的屬性了
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
父組件傳遞了一個引用類型值,為了避免更改父組件中相應的數據,最好是對引用類型進行復制。復雜的情況,肯定應該是深復制。
4、給子組件傳遞正確類型的值
同樣是上面的原因,靜態的給子組件的特性傳遞值,它都會把他當做一個字符串。
<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>
子組件中,特性的值是字符串 "1" 而不是 number 1。如果想傳遞正確的數值,應該使用v-bind傳遞,這樣就能把傳遞的值當做一個表達式來處理,而不是字符串。
<!-- 傳遞實際的 number 1 -->
<comp v-bind:some-prop="1"></comp>
5、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類型會報錯
} })
6、非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>
上面渲染的結果是,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;。
7、自定義事件
通過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",不然,子組件暴露出來的數據就無法獲取到了
8、動態組件
通過使用保留的 <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 } })
9、保留切換出去的組件,避免重新渲染
如果把切換出去的組件保留在內存中,可以保留它的狀態或避免重新渲染。為此可以添加一個 keep-alive 指令參數:
<keep-alive>
<component :is="currentComponent">
<!-- 非活動組件將被緩存! -->
</component>
</keep-alive>
