# 在本文中,筆者又提煉了以下幾個(gè)重點(diǎn)
- 補(bǔ)償雙向數(shù)據(jù)綁定 Vue.$set
- 數(shù)據(jù)偵聽 Vue.$watch
- 表單綁定修飾符
- 動態(tài)組件
- 基礎(chǔ)組件的自動化全局注冊
- Vue.$emit參數(shù),及與 v-on 事件命名規(guī)范
- Prop傳遞數(shù)據(jù)時(shí)防臟
- 插槽及高復(fù)用組件
?
# 補(bǔ)償雙向數(shù)據(jù)綁定 Vue.$set
? 官網(wǎng)說受JS限制,筆者覺得講的太淺了。相信了解雙向數(shù)據(jù)綁定原理的朋友都知道,Vue 2是依賴原生JS中Object.defineProperty()方法的存取操作符set即數(shù)據(jù)劫持實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)更新。然而對于一些引用類型的數(shù)據(jù),如果操作不是發(fā)生在已經(jīng)定義好的數(shù)據(jù)結(jié)構(gòu)本身,Vue 無法探測普通的新增屬性 (比如 this.myObject.newProperty = 'hi'),我們稱它為不是響應(yīng)式的。如:
(1) 對象屬性的添加和刪除時(shí): 見案例
(2) 利用索引直接設(shè)置數(shù)組的一個(gè)項(xiàng)時(shí): vm.items[2] = 'red'
(3) 修改數(shù)組長度時(shí): vm.items.length = newLen
data () {
return {
user: { name: 'zfs', age: 25 }
}
},
mounted: {
this.user = { name: 'borui' } // 改變對象本身,觸發(fā)setter
this.user.tall = 178 // 新增屬性,未觸發(fā)setter,視圖不更新
}
? 為了解決這個(gè)問題,尤大大重寫了set方法,提供了$setAPI接口。注意不要寫成vm.$set(key, value)形式,這種錯(cuò)誤就略低級了。對比一下原生和API接口
原生方法:
Object.defineProperty(object, 'key’, descriptor)
API 接口:vm.$set(vm.Obj, 'key', 'value')
? 很容易發(fā)現(xiàn),該接口原理將描述符descriptor設(shè)置為set, 將輸入的新值value作為參數(shù)傳遞給set調(diào)用從而手動觸發(fā)數(shù)據(jù)劫持。因此,上述案例需要改成:
mounted () {
this.$set(this.student, "tall", 178)
}
? 但是問題又來了,如果需要一次性增加多個(gè)新的響應(yīng)式屬性,顯然多次調(diào)用$set方法不是個(gè)很好的選擇。通常我們會使用Object.assign()或_extend()來實(shí)現(xiàn)。Vue建議創(chuàng)建一個(gè)新的對象來存放兩個(gè)合并對象的所有屬性(通常用空對象{}),然后再賦值給目標(biāo)元素。而不是直接合并到目標(biāo)元素上。做法即:
vm.user = Object.assign({}, vm.user, {
hobby: 'basketball',
favoriteColor: 'Green'
})
?
# 數(shù)據(jù)偵聽 Vue.$watch
? watch提供了觀察和響應(yīng)實(shí)例上數(shù)據(jù)變動的辦法,當(dāng)有一些數(shù)據(jù)需要跟隨其他數(shù)據(jù)變化而變化時(shí),如子組件某個(gè)數(shù)據(jù)依賴來自于父組件的prop計(jì)算。很直觀的會想到計(jì)算這功能和計(jì)算屬性十分類似。Vue建議用戶使用計(jì)算屬性,除非如下情況:
(1)當(dāng)要執(zhí)行的操作是異步操作時(shí),
(2)相應(yīng)事件是開銷較大的操作時(shí)。
watch: {
question: function (newVal, oldVal) {
this.answer = 'Waiting for you to stop typing...'
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
}
? 當(dāng)觀察的值發(fā)生改變時(shí), 觀察者會接收到兩個(gè)參數(shù):(1) 新值,(2)原先的值。 值得注意的是, watch在組件第一次被掛載時(shí)不會觸發(fā), 只有值被改變時(shí)才觸發(fā)。
watch: {
selectedVal ( newVal, oldVal ) {
console.log(newVal)
}
}
?
# data選項(xiàng)為什么是一個(gè)函數(shù)?
? Vue官網(wǎng)第一課描述的data選項(xiàng)就是一個(gè)對象,為什么在編寫組件的時(shí)候卻要定義成一個(gè)函數(shù)?
? 我們知道對象是引用類型,而組件最大的特性就是可復(fù)用性,當(dāng)一個(gè)組件被多次復(fù)用卻指向同一個(gè)引用類型數(shù)據(jù),組件間將無獨(dú)立性而言。因此,將data選項(xiàng)定義成一個(gè)函數(shù),是為了利用函數(shù)的私有作用域特性實(shí)現(xiàn)不同組件間數(shù)據(jù)私有的效果
?
# 計(jì)算屬性緩存 及 get()/set()方法
? 一個(gè)需要計(jì)算的數(shù)據(jù),通常有: (1)計(jì)算屬性獲取,(2)定義一個(gè)方法實(shí)現(xiàn)。雖然實(shí)現(xiàn)結(jié)果相同,但前者優(yōu)勢在于計(jì)算屬性是基于它們的依賴進(jìn)行緩存的。也就是說:
(1)計(jì)算屬性依賴不改變,計(jì)算就不會觸發(fā),改變了才重新觸發(fā)計(jì)算;而調(diào)用方法總會再次執(zhí)行函數(shù)
(2)當(dāng)依賴不是響應(yīng)式依賴時(shí), 計(jì)算屬性將永遠(yuǎn)不會觸發(fā)計(jì)算。如
computed: {
now () {
return Date.now()
}
}
? 計(jì)算屬性默認(rèn)只有 getter,常規(guī)用法其實(shí)是調(diào)用了計(jì)算屬性的getter方法。
? 什么情況下使用setter?一般計(jì)算屬性都是根據(jù)依賴來計(jì)算自身的值,如果計(jì)算屬性自身需要手動傳入值時(shí),就需要提供一個(gè)setter。例如:將一個(gè)計(jì)算屬性綁定給v-model。
? 提供get()、set()的計(jì)算屬性, 需要調(diào)整為一個(gè)對象。
<template>
<input v-model="name" />
</template>
computed: {
reserve : {
get () {
return this. $store.state.name
},
set (val) {
return this.$store.state.name = `李${val}`
}
}
}
?
# v-if 惰性、緩存 及 使用 <template>
? 我們知道,v-if能決定DOM結(jié)構(gòu)存不存在,而v-show只是控制了DOM元素的display屬性,當(dāng)頁面切換頻率不高時(shí),Vue建議使用v-if。
? 所謂的惰性,就是當(dāng)遇到條件為非真時(shí)直接跳過,只有第一次遇到真值才開始渲染條件塊。
? 而緩存,官網(wǎng)給出解釋如下:
Vue 會盡可能高效地渲染元素,通常會復(fù)用已有元素而不是從頭開始渲染。
? 也就是說,假設(shè)頁面原本渲染了一個(gè)input標(biāo)簽,而狀態(tài)改變后也有一個(gè)input標(biāo)簽,Vue檢查到新老標(biāo)簽標(biāo)簽名和屬性列表都相同。將保留已渲染的標(biāo)簽繼續(xù)使用。
? 這種緩存機(jī)制是Vue默認(rèn)的,想修改這種動作,只要給標(biāo)簽加上具有唯一值的key屬性即可,
<input placeholder="Enter your username" key="username-input">
? 正常情況下,v-if會被設(shè)置在一個(gè)標(biāo)簽元素內(nèi)使用,當(dāng)遇到前后兩個(gè)或多個(gè)兄弟標(biāo)簽都需要使用相同狀態(tài)值來判斷是否渲染時(shí),可以一個(gè)無狀態(tài)不可見標(biāo)簽<template>來包裹,Vue在構(gòu)建DOM時(shí)會將其丟棄,并正確的將v-if作用到相應(yīng)的標(biāo)簽上。
<template v-if="real">
<div>實(shí)體車位</div>
<div>實(shí)體車輛</div>
</template>
?
# v-if 與 v-for 優(yōu)先級
? 根據(jù)Vue的風(fēng)格指南,不建議將v-if和v-for放在一起使用,我們來探索一下為什么.
它們一起使用的場景無非就有兩個(gè)
(1)希望通過v-if控制v-for代碼塊是否顯示。這種情況下一般v-if變量是個(gè)狀態(tài)量,與v-for循環(huán)變量無關(guān)。
(2)希望通過循環(huán)變量中的某個(gè)屬性的真假值,來控制該項(xiàng)是否應(yīng)該被循環(huán)渲染出來
這兩種用法有什么問題?在Vue語法中有個(gè)規(guī)則:循環(huán)體中,v-for屬性優(yōu)先級高于其他屬性。也就是說:
場景(1): v-if的渲染會發(fā)生在循環(huán)之后,列表優(yōu)先生成,這就無法提前阻止循環(huán)列表的渲染。這與我們初衷想要決定循環(huán)塊是否渲染產(chǎn)生沖突。解決辦法是:使用<template>標(biāo)簽包裹并在這里設(shè)置v-if控制
場景(2): 如果存在不該被渲染的項(xiàng),這個(gè)項(xiàng)就不應(yīng)該出現(xiàn)在循環(huán)變量中,Vue建議使用計(jì)算屬性過濾數(shù)組。因此也不再需要v-if
?
# v-for 作用于對象
? 循環(huán)不止作用于數(shù)組,同樣可作用于所有可迭代類型變量中。
在遍歷對象時(shí),通常是按 Object.keys() 的結(jié)果遍歷,但是不能保證它的結(jié)果在不同的 JavaScript 引擎下是一致的。
// 對象遍歷,第一個(gè)是值,第二個(gè)是鍵,第三個(gè)才是索引
<div v-for="(value, key, index) in object" :key="index">
{{ key }} : {{ value }}
</div>
?
# v-for渲染后的數(shù)組緩存替換規(guī)則
? Vue 包含一組觀察數(shù)組的變異方法(mutation method),它們會觸發(fā)視圖更新。包括: push()、pop()、shift()、unshift()、splice()、sort()、reserve()等。這些方法都會改變原數(shù)組。
? 同樣還包含非變異方法,如filter()、concat()、slice()。他們不改變原數(shù)組,而是返回一個(gè)新數(shù)組。
? 如果我們對已渲染過后的數(shù)組進(jìn)行非變異方法操作,直覺上列表會重新渲染,其實(shí)不然。
Vue 為了使得 DOM 元素得到最大范圍的重用而實(shí)現(xiàn)了一些智能的、啟發(fā)式的方法,所以用一個(gè)含有相同元素的數(shù)組去替換原來的數(shù)組是非常高效的操作。
exa.items = exa.items.filter(function (item) {
return item.message.match(/Foo/)
})
?
# 凍結(jié)雙向數(shù)據(jù)綁定
? 如果初始渲染后不想讓視圖層響應(yīng)模型層變化, 可以使用v-once標(biāo)簽屬性, 告知被包含在該標(biāo)簽內(nèi)部的所有數(shù)據(jù)綁定不要響應(yīng)視圖更新
<span v-once>這個(gè)數(shù)據(jù)不會發(fā)生改變: {{ message }}</span>
?
# 綁定一段 HTML
? Vue在html部分, 無論是利用雙大括號{{ }}還是v-model綁定的值都會被解釋為普通文本。如果需要綁定一段 HTML,可以使用v-html
<p v-html="htmlCode"></p>
?
# 修飾符
(1) .prevent / .stop / .passive
? 如果你遇到過在頁面執(zhí)行一個(gè)Click事件,觸發(fā)了兩次函數(shù)調(diào)用,你則需要檢查一下是否由事件冒泡引起的。 在DOM2級, DOM3級事件標(biāo)準(zhǔn)中, 瀏覽器接受一個(gè)點(diǎn)擊交互后, 產(chǎn)生事件流會有兩個(gè)過程,捕獲和冒泡。 過程如下:

? 為解決該問題,Vue提供了修飾符.prevent 可以告訴v-on指令對于觸發(fā)的事件調(diào)用event.preventDefault() 來阻止瀏覽器的默認(rèn)行為。 .stop則是調(diào)用event.stopPropagation() 來阻止目標(biāo)元素的冒泡事件
? .passive不能和.prevent一同使用,它會屏蔽.prevent的冒泡效果。.passive主要使用在移動端,它能提高其性能
(2)鍵盤修飾符 .enter / .tab / .delete ...
? Vue提供監(jiān)聽鍵盤按鍵鍵值的辦法,方便我們監(jiān)聽鍵盤事件。一般情況下,直接使用鍵值修飾,如enter鍵的鍵值為13,則使用辦法為:
<input @click.13="handleClick"></input>
? Vue為方便記憶,綁定了常用鍵名與鍵值的關(guān)系可直接使用鍵名綁定
<input @click.enter="handleClick"></input>
常用的有:.enter,.tab,.delete,.esc,.space,.up,.down,.left,.right也可以用通過config.keyCodes 對象自定義按鍵修飾符別名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
(3)鼠標(biāo)修飾符
? 鼠標(biāo)修飾符限制處理函數(shù)僅響應(yīng)特定的鼠標(biāo)按鈕,包括.left,.right,.middle。
?
# 動態(tài)樣式綁定 :class
? Vue允許動態(tài)切換一個(gè)樣式, 支持兩種語法: 對象形式 | 數(shù)組形式
- 對象辦法:鍵表示樣式類名,值為 Truthy 表示添加該樣式
<div class="wrap" :class="{ borderTop: boolean, active: isActive }"></div>
(2)給:class傳遞一個(gè)數(shù)組,表示應(yīng)用一組樣式
<div :class="[ classA, classB ]"></div>
當(dāng)
v-bind:style使用需要添加瀏覽器引擎前綴的 CSS 屬性時(shí),如transform,Vue.js 會自動偵測并添加相應(yīng)的前綴。
?
# 事件綁定傳參
? 如下,前者使用監(jiān)聽事件,而后者是內(nèi)聯(lián)處理器
<div id="example">
<button @click="handleSubmit">提交</button>
<button @click="say('Hi')">問候</button>
</div>
?
# 表單輸入綁定
? 對于普通元素如<div> {{ message }} </div>等并沒有真正表現(xiàn)出Vue雙向數(shù)據(jù)綁定的魅力,其只展現(xiàn)了從ViewModel層發(fā)生變化后反饋到View層的單方面特性。而表單輸入的雙向數(shù)據(jù)綁定還增加了用戶交互使得View層發(fā)生改變并響應(yīng)到ViewModel層,真正體現(xiàn)了“雙向”功能。
? v-model可以在表單元素<input>, <textarea>及<select>上創(chuàng)建雙向數(shù)據(jù)綁定。Vue會根據(jù)空間類型自動選取正確的方法更新元素。值得注意的是,v-model會忽略所有表單元素的value, chekcd, selected特性的初始值而總是將Vue實(shí)例的數(shù)據(jù)data選項(xiàng)作為數(shù)據(jù)來源。也就是說,不能通過特性自身賦值綁定到v-model上,而需要在data中手動賦初始值
- 對于單行多行輸入框,經(jīng)
v-model綁定過后的元素在文本區(qū)域中插值并不會生效,Vue只讀綁定中的內(nèi)容。如
<textarea>{{text}}</textarea>
-
單個(gè)復(fù)選框,
v-model綁定到布爾值;而多個(gè)復(fù)選框則綁定到同一個(gè)數(shù)組
# 只有一個(gè)checkbox則v-model輸出true/false
<input type="checkbox" id="jack" value="Jack" v-model="checkedName">
<label for="jack">{{checkedName}}</label> // checkedName: true / false
# 若在此基礎(chǔ)上,再增加一個(gè),則輸出選中的數(shù)組
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
# 輸出checkedName: [jack, mike]
-
單選按鈕,綁定到同一個(gè)字符串,其值是
value所對應(yīng)的值
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label> // 選中時(shí)輸出對應(yīng)value值: One
- 下拉選擇菜單,單選時(shí)綁定到一個(gè)值上,多選時(shí)綁定到一個(gè)數(shù)組
# 單選下拉框去掉 multiple屬性
<select v-model="selected" multiple style="width: 50px;">
<option v-for="opt in options" v-bind:value="opt .value">
{{ opt .text }}
</option>
</select>
<span>Array: {{ selected }}</span>
?
# 表單綁定修飾符
-
.lazy:將
input觸發(fā)的更新延遲至change觸發(fā)
<input v-model.lazy="msg" >
-
.number:將用戶輸入的內(nèi)容轉(zhuǎn)化為數(shù)字,否則總是返回字符串。設(shè)置
type屬性移動端可以調(diào)起數(shù)字鍵盤。如果這個(gè)值無法被parseFloat()解析,則會返回原始的值
<input v-model.number="age" type="number">
- .trim:自動過濾用戶輸入的首尾空白字符
<input v-model.trim="msg">
?
# Vue.$emit參數(shù),及與 v-on 事件命名規(guī)范
? 在剛開始開發(fā)時(shí)可能會思考為什么prop沒有子向父傳遞。不幸運(yùn)的是,prop的逆向會給數(shù)據(jù)流向帶來巨大的維護(hù)和理解困難,這也是為什么Vue封裝了$emit的模式 觸發(fā)事件來取而代之的原因
this.$emit('method-name', param)
第一個(gè)參數(shù)是拋出的事件名,對應(yīng)父級v-on事件名,第二個(gè)參數(shù)是要帶出的數(shù)據(jù),該數(shù)據(jù)使用$event捕獲
<Children @click="$emit('enlarge-text', 0.1)"> Enlarge text </Children >
<blog @enlarge-text="postFontSize += $event"></blog>
通常父組件中會綁定給一個(gè)屬性,該屬性定義為一個(gè)方法且它的第一個(gè)參數(shù)就是被帶出來的數(shù)據(jù)
<blog @enlarge-text="enlargeText"></blog>
methods: {
enlargeText (num) {
this.postFontSize += num
}
}
【注意】不同于組件和prop,經(jīng)$emit拋出的事件名不會被用作一個(gè)JavaScript變量名或?qū)傩悦?,所以就沒有理由使用camelCase(駝峰式)或PascalCase(短線式)。因?yàn)镠TML大小寫不明感因素,v-on事件監(jiān)聽器在DOM模板中實(shí)質(zhì)上會被自動轉(zhuǎn)換為全小寫,如此一來,原本計(jì)劃通過駝峰式轉(zhuǎn)換成的短線式的監(jiān)聽事件名也不可能被觸發(fā)了,所以如果$emit使用駝峰式命名規(guī)則那么你的監(jiān)聽事件也需要駝峰式命名。
? Vue建議使用短線式或全小寫,特別是前者
this.$emit('my-event', params) // 發(fā)起
<my-component v-on:my-event="handleEmit"></my-component> // 接收
?
# 動態(tài)組件
? 比如我們有一個(gè)tab欄,其中有三個(gè)tab頁,點(diǎn)擊不同tab頁需切換至不同的組件下,此時(shí)非常適合使用is來指定不同的組件達(dá)到動態(tài)組件效果,如下。 完整示例
<component :is="currentTabComponent"></component>
? 通過切換不同的tab能夠?qū)崿F(xiàn)不同組件的渲染。注意當(dāng)你每次切換新標(biāo)簽的時(shí)候,Vue都創(chuàng)建了一個(gè)新的currentTabComponent實(shí)例,因此,他不會保留切換前用戶停留的那個(gè)頁面狀態(tài)。通常來說,重新創(chuàng)建實(shí)例的行為是符合預(yù)期的,但也會有需要保留狀態(tài)的時(shí)候,就像是緩存下來一般
>> 使用keep-alive保留狀態(tài)
// 注意使用了keep-alive的組件必須要有name屬性
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
?
# 基礎(chǔ)組件的自動化全局注冊
? 經(jīng)常為了美化頁面效果,我們會對HTML元素做一層封裝,成為基礎(chǔ)組件,可能是一個(gè)輸入框、一個(gè)按鈕又或者是別的。對于這些組件,Vue建議使用具有語義化的規(guī)范命名風(fēng)格,如以Base開頭,BaseButton,BaseIcon, BaseInput等。引入這些組件往往占據(jù)了大量代碼空間
import好幾行,components: {}又有好幾行,但是他們又只是模板中的很小的一部分。
? 在 Vue CLI 3+ 中提供了require.context 通過全局注冊這些非常通用的基礎(chǔ)組件,允許你在應(yīng)用入口文件(src/main.js)全局導(dǎo)入它們
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camlCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其組件目錄的相對路徑
'./components',
// 是否查詢其子目錄
false,
// 匹配基礎(chǔ)組件文件名的正則表達(dá)式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 獲取組件配置
const componentConfig = requireComponent(fileName)
// 獲取組件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 剝?nèi)ノ募_頭的 `./` 和結(jié)尾的擴(kuò)展名
fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
)
)
// 全局注冊組件
Vue.component(
componentName,
// 如果這個(gè)組件選項(xiàng)是通過 `export default` 導(dǎo)出的,
// 那么就會優(yōu)先使用 `.default`,否則回退到使用模塊的根。
componentConfig.default || componentConfig
)
})
? 這里有一個(gè)真實(shí)案例
?
# Prop傳遞數(shù)據(jù)防臟
? 所有的 prop都使得其父子組件形成一個(gè)單向下行綁定,父級prop的更新會流動到子組件中,但反過來不行。這種設(shè)計(jì)辦法是為了防止子組件意外改變父組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解。另外,如果該數(shù)據(jù)還被其他子組件使用,也將受影響,產(chǎn)生泛洪式災(zāi)難。因此不應(yīng)該在子組件中設(shè)計(jì)修改prop數(shù)據(jù)的操作。
在Javascript中對象和數(shù)組都是通過引用傳入的,因此對于引用類型的prop來說,在子組件中修改數(shù)據(jù)本身將直接改變父級的數(shù)據(jù)。
? 常見的試圖改變prop的操作有一下兩種情形:
- 接收的
prop作為一個(gè)初始值,這個(gè)子組件接下來希望將其作為一個(gè)本地的prop數(shù)據(jù)來使用。這種情況下應(yīng)該使用子組件中的data來拷貝一份prop數(shù)據(jù)數(shù)據(jù)
prop: [ 'initialNum' ],
data () {
return {
num: this.initialNum
}
}
- 接收的
prop作為原始的值需要進(jìn)行格式轉(zhuǎn)換。這種情況下,應(yīng)該使用計(jì)算屬性來實(shí)現(xiàn)
props: ['size'],
computed: {
normalizedSize () {
return this.size.trim().toLowerCase()
}
}
? 當(dāng)不需要對prop做改變只是進(jìn)行使用時(shí)可以不用data拷貝,但也需要注意使用,曾經(jīng)遇到將 == 寫成 =,花了不少時(shí)間找bug。當(dāng)系統(tǒng)比較龐大時(shí)這種問題不好找,所以大家一定要細(xì)心實(shí)在不行就多做個(gè)data拷貝。
?
# prop自定義檢查函數(shù)
? Vue允許在進(jìn)行prop傳值時(shí)對值進(jìn)行驗(yàn)證,type可以驗(yàn)證數(shù)據(jù)類型,default可以設(shè)置當(dāng)未傳入時(shí)的默認(rèn)值。除此之外,還允許開發(fā)者們自定義驗(yàn)證函數(shù)
function CheckName (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
? 驗(yàn)證辦法如下
prop: {
userName: CheckName
}
?
# Prop與自身屬性重名問題
? 當(dāng)使用ElementUI或Bootstrap這些第三方插件時(shí),往往他們定義有自己的屬性,如果開發(fā)者們自定義的prop屬性與其發(fā)生重名時(shí),Vue在大多數(shù)情況下,從外部提供給組件的值會替換掉組件內(nèi)部設(shè)置好的值。
? 即假設(shè)存在傳入type="text" 就會替換掉本身type="date" 類型,原來的就會被破壞。慶幸的是, class和style會智能一些,即兩邊的值會合并起來
?
# Prop實(shí)現(xiàn)‘雙向綁定’效果 .sync
? 父組件中的某個(gè)屬性值需要根據(jù)自身利用prop傳遞給子組件,然后在子組件中做一些操作后響應(yīng)回給父組件來更新這樣的需求時(shí),除了使用$emitAPI,不妨試試.sync
? 作為一種語法糖存在,.sync修飾在v-bind上,可以替代prop傳遞數(shù)據(jù)時(shí)v-on:updata:title="titleName"這種寫法,(給屬性增加update:是Vue在這種需求下的推薦用法),后者還需要使用$emit來回傳值this.$emit('update:title', newTitle)。.sync則顯得更加簡便
? 需要注意的是,.sync修飾的屬性不能和表達(dá)是一起使用,如doc.title + "!"
<text-document v-bind:title.sync="doc.title"></text-document
?
# 將原生事件綁定到組件上
? 通常都是在原生的標(biāo)簽上使用事件的綁定,但有時(shí)候,你可能想要在一個(gè)組件的根元素上直接監(jiān)聽一個(gè)原生事件,如使用CubeUI(一般UI庫自身會提供原生的繼承方法)或自定義組件上。這時(shí),你可以使用 v-on 的 .native 修飾符:
<tab-item @click.native=""></tab-item>
?
# Vue 插槽
? Vue插槽非常重要,筆者為其特意編寫了一個(gè)專題,詳情閱讀Vue插槽,高復(fù)用組件
?
# $ref
? 有時(shí)候需要直接訪問一個(gè)子組件或子元素,此時(shí)可以為他賦予一個(gè)ref作為唯一標(biāo)識,通過$refs來訪問
<self-input ref="nameInput"></self-input>
訪問時(shí)使用 this.$refs.nameInput,這樣就可以自由訪問其內(nèi)部數(shù)據(jù)和方法了。這種辦法同樣適用于元素上。
<input ref="innerInput"></input>
比如我們想在父組件中控制子組件中的input框自動獲取焦點(diǎn),可以這么做
method: {
focus: function() {
this.$refs.innerInput.focus()
}
}
當(dāng)
ref和v-ror一起使用時(shí),得到的結(jié)果是包含了對應(yīng)數(shù)據(jù)源的這些子組件的數(shù)組。另外,需要注意的是,$refs只會在組件渲染完成之后生效,并且不是響應(yīng)式的。它并不適用與計(jì)算屬性
?
# $root & $parent
? 在每個(gè)vue實(shí)例中,提供了根實(shí)例和父實(shí)例的數(shù)據(jù)和方法,這只是一種訪問數(shù)據(jù)的實(shí)例,對于小型應(yīng)用來說很方便,跟建議使用Vuex的狀態(tài)管理機(jī)制。
-
$root: 訪問根實(shí)例的數(shù)據(jù)和方法,包括計(jì)算屬性等 -
$parent: 訪問父實(shí)例的數(shù)據(jù)和方法,包括計(jì)算屬性等。修改父組件容易導(dǎo)致難以查找數(shù)據(jù)變更源
?
# 依賴注入provide & inject
? $root 和 $parent只能實(shí)現(xiàn)根級實(shí)例訪問和父級實(shí)例訪問,然而對于跨級的組件間數(shù)據(jù)交互,雖然可以通過$parent一層層傳遞,但這不是一個(gè)好辦法。依賴注入有了用武之地,通過兩個(gè)新的選項(xiàng):provide和inject。
? provide允許我們在當(dāng)前組件中指定想要提供給后代組件的數(shù)據(jù)和方法,表現(xiàn)形式很像data選項(xiàng)
provide () {
return {
getMsg: this.getMsg
}
}
它就像是一個(gè)大范圍的prop,后代組件都可以使用inject選項(xiàng)倆注入它。
inject: [ 'getMsg' ]
通過依賴注入的數(shù)據(jù)也是非響應(yīng)式的,同樣不適用與計(jì)算屬性
?
# 后語
? 本文內(nèi)容大部分來自官網(wǎng),作為提煉和融入筆者的一點(diǎn)思考,如有不對和不理解的地方歡迎與筆者交流和提出質(zhì)疑