傳遞靜態(tài)或動(dòng)態(tài)Prop
傳入靜態(tài)的值:
<blog-post title="My journey with Vue"></blog-post>
這時(shí)候值是一個(gè)字符串
你也可以通過(guò)v-bind動(dòng)態(tài)賦值:
<blog-post v-bind:title="post.title"></blog-post>
這時(shí)候值是一個(gè)js表達(dá)式
傳入一個(gè)布爾值
// 包含該prop沒(méi)有值的情況在內(nèi),都意味著true
<blog-post is-pushlished></blog-post>
<blog-post :is-pushlished="true"></blog-post>
傳入一個(gè)對(duì)象的所有屬性
如果你想要將一個(gè)對(duì)象的所有屬性都作為prop傳入,你可以使用不帶參數(shù)的v-bind。例如,對(duì)于一個(gè)給定的對(duì)象post:
post: {
id: 1,
title: 'My Journey with Vue'
}
// 下面的模版
<blog-post v-bind="post"></blog-post>
// 等價(jià)于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
單向數(shù)據(jù)流
所有prop都使得其父子prop之間形成一個(gè)單向下行綁定:父級(jí)prop的更新會(huì)向下流動(dòng)到子組件中,但是反過(guò)來(lái)不行。這樣會(huì)防止從子組件意外改變父組件的狀態(tài),從而導(dǎo)致你的應(yīng)用數(shù)據(jù)流向難以理解。額外的,每次父級(jí)組件發(fā)生更新時(shí),子組件中所有prop都將會(huì)刷新為最新的值。
總結(jié):prop會(huì)在子組件創(chuàng)建之前傳遞,父組件通過(guò)prop向子組件傳遞基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型,如果是基本數(shù)據(jù)類(lèi)型,這時(shí)候改變props的數(shù)據(jù)就會(huì)報(bào)錯(cuò),如果是引用數(shù)據(jù)類(lèi)型如果改變?cè)紨?shù)據(jù)不會(huì)報(bào)錯(cuò),但是重新賦值或改變某個(gè)屬性值就會(huì)報(bào)錯(cuò)
prop驗(yàn)證
Vuecomponent('my-component', {
props: {
propA: Number,
// 多個(gè)可能的類(lèi)型
propB: [String, Number],
// 是否必傳,如果為true,則父組件必須要傳這個(gè)值,否則報(bào)錯(cuò)
propC: {
type: String,
required: true
},
// 帶有默認(rèn)值的對(duì)象
propD: {
type: Object,
default: () => { message: 'hello' }
},
// 自定義驗(yàn)證函數(shù),如果不符合條件就會(huì)報(bào)錯(cuò)
propE: {
validator: (value) => {
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
注意:prop會(huì)在一個(gè)組件實(shí)例創(chuàng)建之前進(jìn)行驗(yàn)證,所以實(shí)例的屬性(如data、computed等)在default或validator函數(shù)中是不可用的
非prop的特性
一個(gè)非prop特性是指?jìng)飨蛞粋€(gè)組件,但是該組件并沒(méi)有相應(yīng)prop定義的特性。
<bootstrap data-date-picker="activated"></bootstrap>
data-date-picker="activated"這個(gè)特性就會(huì)自動(dòng)添加到<bootstrap>的根元素上。
替換/合并已有的特性
假如子組件<bootstrap-date-input>的模板是這樣的:
<input type="date" class="form-control">
這時(shí)候我們?cè)谧咏M件上添加一個(gè)type和一個(gè)class:
<bootstrap-date-input type="text" class="date-picker"></bootstrap-date-input>
子組件的模版上已經(jīng)有了這兩個(gè)屬性了,這時(shí)候我們發(fā)現(xiàn)子組件內(nèi)部設(shè)置的type被外部的type替換成text,但是class合并成form-control和date-picker。
總結(jié):除了class和style特性外部和內(nèi)部的根元素會(huì)合并起來(lái),其他的值會(huì)被外部替換掉
禁用特性繼承
如果你不希望組件的根元素繼承特性,你可以在組件的選項(xiàng)中設(shè)置inheritAttrs:false。例如:
Vue.component('my-component', {
inheritAttrs:false,
....
})
這尤其適合配合實(shí)例的$attrs屬性使用,該屬性包含了傳遞給一個(gè)組件的特性名和特性值。
自定義事件
model屬性
一個(gè)組件上的v-model默認(rèn)會(huì)利用名為value的prop和名為input的事件,但是像單選框、復(fù)選框等類(lèi)型的輸入控件可能會(huì)將value特性用于不同的目的。model選項(xiàng)可以用來(lái)避免這樣的沖突:
<base-checkbox v-model="lovingVue"></base-checkbox>
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
上面我們可以看到model將從父組件傳過(guò)來(lái)的屬性不管是vlaue還是checked統(tǒng)一名稱(chēng)為checked,監(jiān)聽(tīng)的事件不管是@input還是@change統(tǒng)一為change,這樣子就不需要根據(jù)不同的情況寫(xiě)不同的屬性和方法。
這里的lovingVue的值將會(huì)傳入這個(gè)名為checked的prop。同時(shí)當(dāng)<base-checkbox>觸發(fā)一個(gè)change事件并附帶一個(gè)新的值的時(shí)候,這個(gè)logingVue的屬性將會(huì)被更新。
注意你仍然需要在組件的props選項(xiàng)里聲明checked這個(gè)prop。
.sync修飾符
在有些情況下,我們可能需要對(duì)一個(gè)prop進(jìn)行雙向綁定。不幸的是,真正的雙向綁定會(huì)帶來(lái)維護(hù)上的問(wèn)題,因?yàn)樽咏M件可以修改父組件,造成理解困難。
這時(shí)候我們可以使用.sync修飾符,它的本質(zhì)和v-model類(lèi)似,只是一種縮寫(xiě)。
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
上面的代碼使用.sync就可以寫(xiě)成
<text-document v-bind:title.sync="doc.title"></text-document>
這樣在子組件中,就可以通過(guò)下面的代碼來(lái)實(shí)現(xiàn)對(duì)這個(gè)prop重新賦值的意圖了。
this.$emit('update:title', newTitle)
v-model和.sync背景
父子組件傳遞數(shù)據(jù)通過(guò)props,props傳遞的數(shù)據(jù)是單向數(shù)據(jù)流,但如果傳遞的是引用數(shù)據(jù)類(lèi)型,那么改變子組件傳遞過(guò)來(lái)的值父組件的值也會(huì)發(fā)生改變,這樣子雖然可以實(shí)現(xiàn)父子組件的雙向綁定,但會(huì)犧牲數(shù)據(jù)流向的簡(jiǎn)潔性,使得數(shù)據(jù)難以理解,最好不要這樣去做,在這個(gè)背景下v-model和.sync就出現(xiàn)了,這兩種方式都實(shí)現(xiàn)父子組件數(shù)據(jù)的雙向綁定。
v-mode和.sync對(duì)比
.sync從功能上看和v-model十分類(lèi)似,都是為了實(shí)現(xiàn)數(shù)據(jù)的雙向綁定,本質(zhì)上也都不是真正的雙向綁定,而是語(yǔ)法糖。
相比較之下,.sync更加靈活,它可以給多個(gè)prop使用,而v-model在一個(gè)組件中只能有一個(gè)。
從語(yǔ)義上來(lái)看,v-model綁定的值是指這個(gè)組件的綁定值,比如input組件,select組件,各種表單元素,這些組件所綁定的值使用v-model比較合適,因?yàn)関-model是一個(gè)語(yǔ)法糖,傳遞的各種類(lèi)型的值比如vlaue、checked等等,vue已經(jīng)處理了各種情況,不需要自己去分別定義傳遞的屬性。其他情況沒(méi)有這種語(yǔ)義,.sync會(huì)更合適。
從寫(xiě)法上來(lái)看,.sync更加簡(jiǎn)潔,不需要在 子組件中定義props就可以直接實(shí)現(xiàn)數(shù)據(jù)雙向綁定。
這兩種方式子組件都沒(méi)有直接改變prop的值,只是通過(guò)事件監(jiān)聽(tīng)的方式直接改變父組件的值。
注意帶有 .sync 修飾符的 v-bind 不能和表達(dá)式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是無(wú)效的)。取而代之的是,你只能提供你想要綁定的屬性名,類(lèi)似 v-model。
當(dāng)我們用一個(gè)對(duì)象同時(shí)設(shè)置多個(gè) prop 的時(shí)候,也可以將這個(gè) .sync 修飾符和 v-bind 配合使用:
<text-document v-bind.sync="doc"></text-document>
這樣會(huì)把 doc 對(duì)象中的每一個(gè)屬性 (如 title) 都作為一個(gè)獨(dú)立的 prop 傳進(jìn)去,然后各自添加用于更新的 v-on 監(jiān)聽(tīng)器。
將 v-bind.sync 用在一個(gè)字面量的對(duì)象上,例如 v-bind.sync=”{ title: doc.title }”,是無(wú)法正常工作的,因?yàn)樵诮馕鲆粋€(gè)像這樣的復(fù)雜表達(dá)式的時(shí)候,有很多邊緣情況需要考慮。
參考文章:https://juejin.im/post/5d0489dff265da1ba84a8e41
插槽
我們?yōu)榫呙宀酆妥饔糜虿宀垡肓艘粋€(gè)新的統(tǒng)一的語(yǔ)法,即v-slot指令。它取代了slot和slot-scope這兩個(gè)目前已被廢棄但未被移除且仍在文檔中的特性。
插槽內(nèi)容
Vue實(shí)現(xiàn)了一套內(nèi)容分發(fā)的API,將slot元素作為承載分發(fā)內(nèi)容的出口,也被稱(chēng)為插槽,插槽的內(nèi)容是寫(xiě)在自定義組件中間,插槽內(nèi)容可以是包含任何模版代碼或其他組件,這些內(nèi)容會(huì)將slot元素替換,如果自定義組件內(nèi)沒(méi)有插槽,那么這個(gè)組件中間寫(xiě)的任何內(nèi)容都會(huì)被拋棄。
編譯作用域
當(dāng)你想在一個(gè)插槽內(nèi)容中使用數(shù)據(jù)時(shí),例如:
<template>
<div id="demo">
<navigation-link url="/profile">
name: {{name}}
url: {{url}}
</navigation-link>
</div>
</template>
<script>
import navigationLink from './navigationLink'
export default {
components: {navigationLink},
data () {
return {
name: 'jack'
}
}
}
</script>
你會(huì)發(fā)現(xiàn)插槽內(nèi)容跟模版其他地方一樣可以訪(fǎng)問(wèn)相同的實(shí)例屬性(也就是相同的作用域),而不能訪(fǎng)問(wèn)navigation-link子組件的作用域。
作為一條規(guī)則,請(qǐng)記?。?strong>父級(jí)模版里的所有內(nèi)容都是在父級(jí)作用域中編譯的,子模版里的所有內(nèi)容都是在子作用域中編譯的。
后備內(nèi)容(備用內(nèi)容)
如果插槽內(nèi)沒(méi)有內(nèi)容,我們希望能顯示默認(rèn)的內(nèi)容,這時(shí)候我們可以在slot元素中寫(xiě),如果沒(méi)有插槽內(nèi)容就會(huì)顯示slot元素中內(nèi)容,如果有內(nèi)容就會(huì)替換slot元素中的內(nèi)容。
具名插槽
有時(shí)我們需要多個(gè)插槽,例如對(duì)于一個(gè)帶有如下模版的<base-layout>組件:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一個(gè)不帶name的slot出口會(huì)帶有隱含的名字default。
在向具名插槽提供內(nèi)容的時(shí)候,我們可以在一個(gè)template元素上使用v-slot指令,并以v-slot的參數(shù)的形式提供其名稱(chēng):
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
現(xiàn)在 <template> 元素中的所有內(nèi)容都將會(huì)被傳入相應(yīng)的插槽。任何沒(méi)有被包裹在帶有 v-slot 的 <template> 中的內(nèi)容都會(huì)被視為默認(rèn)插槽的內(nèi)容。
注意:v-slot只能添加在一個(gè)template元素上(只有一種例外情況就是獨(dú)占默認(rèn)插槽的縮寫(xiě)語(yǔ)法,下面會(huì)講到)
作用域插槽
如果我們想讓插槽內(nèi)容訪(fǎng)問(wèn)組件內(nèi)的數(shù)據(jù),這時(shí)候我們可以使用作用域插槽,將數(shù)據(jù)作為slot元素的一個(gè)特性綁定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
綁定在slot元素上的特性被稱(chēng)為插槽porp。在父級(jí)作用域中,我們可以給v-slot帶一個(gè)值來(lái)定義我們提供的插槽prop的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
獨(dú)占默認(rèn)插槽的縮寫(xiě)語(yǔ)法
當(dāng)組件內(nèi)只有默認(rèn)插槽時(shí),組件的標(biāo)簽就可以被當(dāng)作插槽的模版來(lái)使用。這樣我們就可以把v-slot直接用在組件上:
<current-user v-slot:default="slotProps">
{{slotProps.user.firstName}}
</current-user>
// 不帶參數(shù)的v-slot被假定對(duì)應(yīng)默認(rèn)插槽,可以簡(jiǎn)寫(xiě)為
<current-user v-slot="slotProps">
{{slotProps.user.firstName}}
</current-user>
注意默認(rèn)插槽的縮寫(xiě)語(yǔ)法不能和具名插槽混用,因?yàn)樗鼤?huì)導(dǎo)致作用域不明確,只要出現(xiàn)多個(gè)插槽,請(qǐng)始終為所有的插槽使用完整的基于<template>的語(yǔ)法。
解構(gòu)插槽prop
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
// prop重命名
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
// 插槽prop是undefined
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
動(dòng)態(tài)插槽名
動(dòng)態(tài)指令參數(shù)也可以用在v-slot上,來(lái)定義動(dòng)態(tài)的插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
具名插槽的縮寫(xiě)
跟v-on和v-bind一樣,v-slot也有縮寫(xiě),即把參數(shù)之前的所有內(nèi)容(v-slot:)替換為字符#。例如v-slot:header可以重寫(xiě)為#header:
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
</base-layout>
如果你希望使用縮寫(xiě)的話(huà),你必須始終以明確插槽名取而代之:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
總結(jié):插槽有點(diǎn)像一個(gè)單獨(dú)的組件,可以定義它的名稱(chēng),也可以傳值給插槽內(nèi)容,插槽在設(shè)計(jì)基礎(chǔ)組件的時(shí)候非常有用,比如我之前寫(xiě)的彈框組件,element-ui中的很多組件都有用到插槽,你可以在組件里面寫(xiě)你自定義的其他組件。
處理邊界的情況
在大多數(shù)情況下,我們最好不要觸達(dá)另一個(gè)組件實(shí)例內(nèi)部或手動(dòng)操作DOM元素。比如使用$refs、$parent、$children、$root等等,最好使用Vuex來(lái)管理應(yīng)用的狀態(tài)。
混入
混入提供了一種非常靈活的方式,來(lái)分發(fā)Vue組件中的可復(fù)用功能。
選項(xiàng)合并
當(dāng)組件和混入對(duì)象含有同名選項(xiàng)時(shí),這些選項(xiàng)將以恰當(dāng)?shù)姆绞竭M(jìn)行合并。
data數(shù)據(jù)合并
data中數(shù)據(jù)有同名選項(xiàng)時(shí),如果是基本類(lèi)型就會(huì)以組件數(shù)據(jù)為準(zhǔn),如果是引用類(lèi)型就會(huì)合并。
生命周期合并
生命周期里的代碼都會(huì)執(zhí)行,并且混入對(duì)象的代碼會(huì)先執(zhí)行。
methods、components、directives
這三個(gè)對(duì)象將被合并為同一個(gè)對(duì)象,如果發(fā)生同名選項(xiàng),以組件的為準(zhǔn)。
注意:Vue.extend()也使用同樣的策略進(jìn)行合并。
自定義指令
基本用法
除了核心功能默認(rèn)內(nèi)置的指令比如v-model、v-show,Vue也允許注冊(cè)自定義指令。一般用在需要對(duì)普通DOM元素進(jìn)行底層操作,這時(shí)候就會(huì)用到自定義指令。比如業(yè)務(wù)需求是input輸入框初始是聚焦的狀態(tài):
// 注冊(cè)一個(gè)全局自定義指令v-focus
Vue.directive('focus', {
// 當(dāng)被綁定的元素插入到DOM中時(shí)
inserted (el, binding) {
el.focus()
}
})
// 注冊(cè)一個(gè)局部的指令
directives: {
focus: {
inserted (el, binding) {
el.focus()
}
}
}
動(dòng)態(tài)指令參數(shù)
指令的參數(shù)可以是動(dòng)態(tài)的。例如,在v-myditective:[argument]="value"中,argument參數(shù)可以根據(jù)組件實(shí)例數(shù)據(jù)進(jìn)行更新!這使得自定義指令可以在應(yīng)用中被靈活使用。
<div id="dynamicexample">
<h3>Scroll down inside this section ↓</h3>
<p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
</div>
Vue.directive('pin', {
bind: function (el, binding, vnode) {
el.style.position = 'fixed'
var s = (binding.arg == 'left' ? 'left' : 'top')
el.style[s] = binding.value + 'px'
}
})
new Vue({
el: '#dynamicexample',
data: function () {
return {
direction: 'left'
}
}
})
過(guò)濾器
Vue允許你自定義過(guò)濾器,可被用于一些常見(jiàn)的文本格式化,或者是一些數(shù)據(jù)轉(zhuǎn)換等等,但是過(guò)濾器在實(shí)踐當(dāng)中可以被其他方式取代,比如寫(xiě)一些公共方法去處理或者直接直接在methods去調(diào)用某個(gè)方法等等,所以現(xiàn)在用到的越來(lái)越少,這個(gè)功能就顯得有點(diǎn)雞肋。不過(guò)它有一個(gè)優(yōu)勢(shì)是別的方法實(shí)現(xiàn)比較麻煩的,鏈?zhǔn)秸{(diào)用,后面會(huì)講到。
基本用法
過(guò)濾器可以用在兩個(gè)地方:雙花括號(hào)插值和v-bind表達(dá)式。
// 在雙花括號(hào)中
{{ message | capitalize }}
// 在v-bind中
<div v-bind="rawId | formatId"></div>
你可以在一個(gè)組件的選項(xiàng)中定義本地的過(guò)濾器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者在創(chuàng)建Vue實(shí)例之前全局 定義過(guò)濾器:
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
過(guò)濾器可以串聯(lián):
{{ message | filterA('arg1', 'arg2') | filterB }}
在這個(gè)例子中,message表達(dá)式將作為filterA過(guò)濾器函數(shù)的第一個(gè)參數(shù),'arg1'和'arg2'將作為第二個(gè)、第三個(gè)參數(shù),filterA函數(shù)返回的結(jié)果又會(huì)作為filterB函數(shù)的第一個(gè)參數(shù)。
key
key的特殊屬性主要用在Vue虛擬DOM算法,在新舊nodes對(duì)比時(shí)辨識(shí)vnodes。如果不實(shí)用key,Vue會(huì)使用一種最大限度減少動(dòng)態(tài)元素并且盡可能的嘗試修復(fù)/再利用相同類(lèi)型元素的算法。使用key,它會(huì)基于key的變化重新排列元素順序,并且會(huì)移除key不存在的元素。
有相同父元素的子元素必須有特殊的key。重復(fù)的key會(huì)造成渲染錯(cuò)誤。它可以用于強(qiáng)制替換元素/組件而不是重復(fù)使用它。
Vue.extend()
使用基礎(chǔ)Vue構(gòu)造器,創(chuàng)建一個(gè)'子類(lèi)'。參數(shù)是一個(gè)包含組件選項(xiàng)的對(duì)象,data選項(xiàng)必須是函數(shù)。
<div id="mount-point"></div>
// 創(chuàng)建構(gòu)造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 創(chuàng)建 Profile 實(shí)例,并掛載到一個(gè)元素上。
new Profile().$mount('#mount-point')
結(jié)果如下:
<p>Walter White aka Heisenberg</p>
Vue.observable()
讓一個(gè)對(duì)象可響應(yīng)。Vue內(nèi)部會(huì)用它來(lái)處理data函數(shù)返回的對(duì)象。
返回的對(duì)象可以直接用于渲染函數(shù)和計(jì)算屬性?xún)?nèi),并且會(huì)在發(fā)生改變時(shí)觸發(fā)相應(yīng)的更新。也可以作為最小化的跨組件狀態(tài)存儲(chǔ)器,用于簡(jiǎn)單的場(chǎng)景:
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `count is: ${state.count}`)
}
}