Vue 朝花夕拾(基礎(chǔ)與組件)

# 在本文中盲链,筆者又提煉了以下幾個(gè)重點(diǎn)

  1. 補(bǔ)償雙向數(shù)據(jù)綁定 Vue.$set
  2. 數(shù)據(jù)偵聽(tīng) Vue.$watch
  3. 表單綁定修飾符
  4. 動(dòng)態(tài)組件
  5. 基礎(chǔ)組件的自動(dòng)化全局注冊(cè)
  6. Vue.$emit參數(shù),及與 v-on 事件命名規(guī)范
  7. Prop傳遞數(shù)據(jù)時(shí)防臟
  8. 插槽及高復(fù)用組件

?

# 補(bǔ)償雙向數(shù)據(jù)綁定 Vue.$set

? 官網(wǎng)說(shuō)受JS限制,筆者覺(jué)得講的太淺了捂龄。相信了解雙向數(shù)據(jù)綁定原理的朋友都知道九串,Vue 2是依賴(lài)原生JS中Object.defineProperty()方法的存取操作符set即數(shù)據(jù)劫持實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)更新。然而對(duì)于一些引用類(lèi)型的數(shù)據(jù)唱星,如果操作不是發(fā)生在已經(jīng)定義好的數(shù)據(jù)結(jié)構(gòu)本身喊衫,Vue 無(wú)法探測(cè)普通的新增屬性 (比如 this.myObject.newProperty = 'hi')跌造,我們稱(chēng)它為不是響應(yīng)式的。如:

(1) 對(duì)象屬性的添加和刪除時(shí): 見(jiàn)案例
(2) 利用索引直接設(shè)置數(shù)組的一個(gè)項(xiàng)時(shí): vm.items[2] = 'red'
(3) 修改數(shù)組長(zhǎng)度時(shí): vm.items.length = newLen

data () {
  return {
    user: { name: 'zfs', age: 25  }
  }
},
mounted: {
  this.user = { name: 'borui' }  // 改變對(duì)象本身族购,觸發(fā)setter
  this.user.tall = 178  // 新增屬性,未觸發(fā)setter陵珍,視圖不更新
}

? 為了解決這個(gè)問(wèn)題寝杖,尤大大重寫(xiě)了set方法,提供了$setAPI接口互纯。注意不要寫(xiě)成vm.$set(key, value)形式瑟幕,這種錯(cuò)誤就略低級(jí)了。對(duì)比一下原生和API接口

原生方法:Object.defineProperty(object, 'key’, descriptor)
API 接口: vm.$set(vm.Obj, 'key', 'value')

? 很容易發(fā)現(xiàn),該接口原理將描述符descriptor設(shè)置為set, 將輸入的新值value作為參數(shù)傳遞給set調(diào)用從而手動(dòng)觸發(fā)數(shù)據(jù)劫持只盹。因此辣往,上述案例需要改成:

mounted () {
  this.$set(this.student, "tall",  178)
}

? 但是問(wèn)題又來(lái)了,如果需要一次性增加多個(gè)新的響應(yīng)式屬性殖卑,顯然多次調(diào)用$set方法不是個(gè)很好的選擇站削。通常我們會(huì)使用Object.assign()_extend()來(lái)實(shí)現(xiàn)。Vue建議創(chuàng)建一個(gè)新的對(duì)象來(lái)存放兩個(gè)合并對(duì)象的所有屬性(通常用空對(duì)象{})孵稽,然后再賦值給目標(biāo)元素许起。而不是直接合并到目標(biāo)元素上。做法即:

vm.user = Object.assign({}, vm.user, {
  hobby: 'basketball',
  favoriteColor: 'Green'
})

?

# 數(shù)據(jù)偵聽(tīng) Vue.$watch

? watch提供了觀察和響應(yīng)實(shí)例上數(shù)據(jù)變動(dòng)的辦法菩鲜,當(dāng)有一些數(shù)據(jù)需要跟隨其他數(shù)據(jù)變化而變化時(shí)园细,如子組件某個(gè)數(shù)據(jù)依賴(lài)來(lái)自于父組件的prop計(jì)算。很直觀的會(huì)想到計(jì)算這功能和計(jì)算屬性十分類(lèi)似接校。Vue建議用戶(hù)使用計(jì)算屬性猛频,除非如下情況:
(1)當(dāng)要執(zhí)行的操作是異步操作時(shí),
(2)相應(yīng)事件是開(kāi)銷(xiāo)較大的操作時(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í), 觀察者會(huì)接收到兩個(gè)參數(shù):(1) 新值伦乔,(2)原先的值。 值得注意的是, watch在組件第一次被掛載時(shí)不會(huì)觸發(fā), 只有值被改變時(shí)才觸發(fā)董习。

watch: {
  selectedVal ( newVal, oldVal ) {
    console.log(newVal)
  }
}

?

# data選項(xiàng)為什么是一個(gè)函數(shù)烈和?

? Vue官網(wǎng)第一課描述的data選項(xiàng)就是一個(gè)對(duì)象,為什么在編寫(xiě)組件的時(shí)候卻要定義成一個(gè)函數(shù)皿淋?

? 我們知道對(duì)象是引用類(lèi)型招刹,而組件最大的特性就是可復(fù)用性,當(dāng)一個(gè)組件被多次復(fù)用卻指向同一個(gè)引用類(lèi)型數(shù)據(jù)窝趣,組件間將無(wú)獨(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)勢(shì)在于計(jì)算屬性是基于它們的依賴(lài)進(jìn)行緩存的。也就是說(shuō):

(1)計(jì)算屬性依賴(lài)不改變膘滨,計(jì)算就不會(huì)觸發(fā)甘凭,改變了才重新觸發(fā)計(jì)算;而調(diào)用方法總會(huì)再次執(zhí)行函數(shù)
(2)當(dāng)依賴(lài)不是響應(yīng)式依賴(lài)時(shí), 計(jì)算屬性將永遠(yuǎn)不會(huì)觸發(fā)計(jì)算火邓。如

computed: {
  now () {
    return Date.now()
  }
}

? 計(jì)算屬性默認(rèn)只有 getter丹弱,常規(guī)用法其實(shí)是調(diào)用了計(jì)算屬性的getter方法德撬。
? 什么情況下使用setter?一般計(jì)算屬性都是根據(jù)依賴(lài)來(lái)計(jì)算自身的值躲胳,如果計(jì)算屬性自身需要手動(dòng)傳入值時(shí)蜓洪,就需要提供一個(gè)setter。例如:將一個(gè)計(jì)算屬性綁定給v-model坯苹。
? 提供get()隆檀、set()的計(jì)算屬性, 需要調(diào)整為一個(gè)對(duì)象。

<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)頁(yè)面切換頻率不高時(shí)菊霜,Vue建議使用v-if
? 所謂的惰性济赎,就是當(dāng)遇到條件為非真時(shí)直接跳過(guò)鉴逞,只有第一次遇到真值才開(kāi)始渲染條件塊。
? 而緩存司训,官網(wǎng)給出解釋如下:

Vue 會(huì)盡可能高效地渲染元素构捡,通常會(huì)復(fù)用已有元素而不是從頭開(kāi)始渲染。

? 也就是說(shuō)壳猜,假設(shè)頁(yè)面原本渲染了一個(gè)input標(biāo)簽勾徽,而狀態(tài)改變后也有一個(gè)input標(biāo)簽,Vue檢查到新老標(biāo)簽標(biāo)簽名和屬性列表都相同统扳。將保留已渲染的標(biāo)簽繼續(xù)使用喘帚。
? 這種緩存機(jī)制是Vue默認(rèn)的,想修改這種動(dòng)作咒钟,只要給標(biāo)簽加上具有唯一值的key屬性即可吹由,

<input placeholder="Enter your username" key="username-input">

? 正常情況下,v-if會(huì)被設(shè)置在一個(gè)標(biāo)簽元素內(nèi)使用朱嘴,當(dāng)遇到前后兩個(gè)或多個(gè)兄弟標(biāo)簽都需要使用相同狀態(tài)值來(lái)判斷是否渲染時(shí)倾鲫,可以一個(gè)無(wú)狀態(tài)不可見(jiàn)標(biāo)簽<template>來(lái)包裹,Vue在構(gòu)建DOM時(shí)會(huì)將其丟棄萍嬉,并正確的將v-if作用到相應(yīng)的標(biāo)簽上乌昔。

<template v-if="real">
  <div>實(shí)體車(chē)位</div>
  <div>實(shí)體車(chē)輛</div>
</template>

?

# v-if 與 v-for 優(yōu)先級(jí)

? 根據(jù)Vue的風(fēng)格指南,不建議將v-ifv-for放在一起使用帚湘,我們來(lái)探索一下為什么.

它們一起使用的場(chǎng)景無(wú)非就有兩個(gè)
(1)希望通過(guò)v-if控制v-for代碼塊是否顯示玫荣。這種情況下一般v-if變量是個(gè)狀態(tài)量,與v-for循環(huán)變量無(wú)關(guān)大诸。
(2)希望通過(guò)循環(huán)變量中的某個(gè)屬性的真假值捅厂,來(lái)控制該項(xiàng)是否應(yīng)該被循環(huán)渲染出來(lái)

這兩種用法有什么問(wèn)題?在Vue語(yǔ)法中有個(gè)規(guī)則:循環(huán)體中资柔,v-for屬性?xún)?yōu)先級(jí)高于其他屬性焙贷。也就是說(shuō):
場(chǎng)景(1): v-if的渲染會(huì)發(fā)生在循環(huán)之后,列表優(yōu)先生成贿堰,這就無(wú)法提前阻止循環(huán)列表的渲染辙芍。這與我們初衷想要決定循環(huán)塊是否渲染產(chǎn)生沖突。解決辦法是:使用<template>標(biāo)簽包裹并在這里設(shè)置v-if控制
場(chǎng)景(2): 如果存在不該被渲染的項(xiàng)羹与,這個(gè)項(xiàng)就不應(yīng)該出現(xiàn)在循環(huán)變量中故硅,Vue建議使用計(jì)算屬性過(guò)濾數(shù)組。因此也不再需要v-if
?

# v-for 作用于對(duì)象

? 循環(huán)不止作用于數(shù)組纵搁,同樣可作用于所有可迭代類(lèi)型變量中吃衅。

在遍歷對(duì)象時(shí),通常是按 Object.keys() 的結(jié)果遍歷腾誉,但是不能保證它的結(jié)果在不同的 JavaScript 引擎下是一致的徘层。

// 對(duì)象遍歷,第一個(gè)是值利职,第二個(gè)是鍵趣效,第三個(gè)才是索引
<div v-for="(value, key, index) in object" :key="index">
  {{ key }} : {{ value }}
</div>

?

# v-for渲染后的數(shù)組緩存替換規(guī)則

? Vue 包含一組觀察數(shù)組的變異方法(mutation method),它們會(huì)觸發(fā)視圖更新猪贪。包括: push()跷敬、pop()、shift()热押、unshift()西傀、splice()、sort()楞黄、reserve()等池凄。這些方法都會(huì)改變?cè)瓟?shù)組。
? 同樣還包含非變異方法鬼廓,如filter()肿仑、concat()、slice()碎税。他們不改變?cè)瓟?shù)組尤慰,而是返回一個(gè)新數(shù)組。

? 如果我們對(duì)已渲染過(guò)后的數(shù)組進(jìn)行非變異方法操作雷蹂,直覺(jué)上列表會(huì)重新渲染伟端,其實(shí)不然。

Vue 為了使得 DOM 元素得到最大范圍的重用而實(shí)現(xiàn)了一些智能的匪煌、啟發(fā)式的方法责蝠,所以用一個(gè)含有相同元素的數(shù)組去替換原來(lái)的數(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ù)不會(huì)發(fā)生改變: {{ message }}</span>

?

# 綁定一段 HTML

? Vue在html部分, 無(wú)論是利用雙大括號(hào){{ }}還是v-model綁定的值都會(huì)被解釋為普通文本。如果需要綁定一段 HTML霜医,可以使用v-html

<p v-html="htmlCode"></p>

?

# 修飾符

(1) .prevent / .stop / .passive

? 如果你遇到過(guò)在頁(yè)面執(zhí)行一個(gè)Click事件齿拂,觸發(fā)了兩次函數(shù)調(diào)用,你則需要檢查一下是否由事件冒泡引起的肴敛。 在DOM2級(jí), DOM3級(jí)事件標(biāo)準(zhǔn)中, 瀏覽器接受一個(gè)點(diǎn)擊交互后, 產(chǎn)生事件流會(huì)有兩個(gè)過(guò)程署海,捕獲和冒泡。 過(guò)程如下:

? 為解決該問(wèn)題医男,Vue提供了修飾符.prevent 可以告訴v-on指令對(duì)于觸發(fā)的事件調(diào)用event.preventDefault() 來(lái)阻止瀏覽器的默認(rèn)行為砸狞。 .stop則是調(diào)用event.stopPropagation() 來(lái)阻止目標(biāo)元素的冒泡事件

? .passive不能和.prevent一同使用,它會(huì)屏蔽.prevent的冒泡效果镀梭。.passive主要使用在移動(dòng)端刀森,它能提高其性能

(2)鍵盤(pán)修飾符 .enter / .tab / .delete ...

? Vue提供監(jiān)聽(tīng)鍵盤(pán)按鍵鍵值的辦法,方便我們監(jiān)聽(tīng)鍵盤(pá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也可以用通過(guò)config.keyCodes 對(duì)象自定義按鍵修飾符別名

// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
(3)鼠標(biāo)修飾符

? 鼠標(biāo)修飾符限制處理函數(shù)僅響應(yīng)特定的鼠標(biāo)按鈕腕扶,包括.left.right吨掌,.middle半抱。
?

# 動(dòng)態(tài)樣式綁定 :class

? Vue允許動(dòng)態(tài)切換一個(gè)樣式, 支持兩種語(yǔ)法: 對(duì)象形式 | 數(shù)組形式

  • 對(duì)象辦法:鍵表示樣式類(lèi)名,值為 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 會(huì)自動(dòng)偵測(cè)并添加相應(yīng)的前綴秋茫。

?

# 事件綁定傳參

? 如下史简,前者使用監(jiān)聽(tīng)事件,而后者是內(nèi)聯(lián)處理器

<div id="example">
  <button @click="handleSubmit">提交</button>
  <button @click="say('Hi')">問(wèn)候</button>
</div>

?

# 表單輸入綁定

? 對(duì)于普通元素如<div> {{ message }} </div>等并沒(méi)有真正表現(xiàn)出Vue雙向數(shù)據(jù)綁定的魅力肛著,其只展現(xiàn)了從ViewModel層發(fā)生變化后反饋到View層的單方面特性圆兵。而表單輸入的雙向數(shù)據(jù)綁定還增加了用戶(hù)交互使得View層發(fā)生改變并響應(yīng)到ViewModel層跺讯,真正體現(xiàn)了“雙向”功能。

? v-model可以在表單元素<input>, <textarea><select>上創(chuàng)建雙向數(shù)據(jù)綁定衙傀。Vue會(huì)根據(jù)空間類(lèi)型自動(dòng)選取正確的方法更新元素抬吟。值得注意的是萨咕,v-model會(huì)忽略所有表單元素的value, chekcd, selected特性的初始值而總是將Vue實(shí)例的數(shù)據(jù)data選項(xiàng)作為數(shù)據(jù)來(lái)源统抬。也就是說(shuō),不能通過(guò)特性自身賦值綁定到v-model上危队,而需要在data中手動(dòng)賦初始值

  1. 對(duì)于單行多行輸入框聪建,經(jīng)v-model綁定過(guò)后的元素在文本區(qū)域中插值并不會(huì)生效,Vue只讀綁定中的內(nèi)容茫陆。如
<textarea>{{text}}</textarea>
  1. 單個(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]
  1. 單選按鈕,綁定到同一個(gè)字符串桨醋,其值是value所對(duì)應(yīng)的值
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>    // 選中時(shí)輸出對(duì)應(yīng)value值: One
  1. 下拉選擇菜單棚瘟,單選時(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:將用戶(hù)輸入的內(nèi)容轉(zhuǎn)化為數(shù)字喜最,否則總是返回字符串偎蘸。設(shè)置type屬性移動(dòng)端可以調(diào)起數(shù)字鍵盤(pán)。如果這個(gè)值無(wú)法被 parseFloat()解析瞬内,則會(huì)返回原始的值
<input v-model.number="age" type="number">
  • .trim:自動(dòng)過(guò)濾用戶(hù)輸入的首尾空白字符
<input v-model.trim="msg">

?

# Vue.$emit參數(shù)迷雪,及與 v-on 事件命名規(guī)范

? 在剛開(kāi)始開(kāi)發(fā)時(shí)可能會(huì)思考為什么prop沒(méi)有子向父?jìng)鬟f。不幸運(yùn)的是虫蝶,prop的逆向會(huì)給數(shù)據(jù)流向帶來(lái)巨大的維護(hù)和理解困難章咧,這也是為什么Vue封裝了$emit的模式 觸發(fā)事件來(lái)取而代之的原因

this.$emit('method-name', param)

第一個(gè)參數(shù)是拋出的事件名,對(duì)應(yīng)父級(jí)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>

通常父組件中會(huì)綁定給一個(gè)屬性,該屬性定義為一個(gè)方法且它的第一個(gè)參數(shù)就是被帶出來(lái)的數(shù)據(jù)

<blog @enlarge-text="enlargeText"></blog>

methods: {
  enlargeText  (num) {
    this.postFontSize += num
  }
}

注意】不同于組件和prop舟陆,經(jīng)$emit拋出的事件名不會(huì)被用作一個(gè)JavaScript變量名或?qū)傩悦蟀模跃蜎](méi)有理由使用camelCase(駝峰式)或PascalCase(短線(xiàn)式)。因?yàn)镠TML大小寫(xiě)不明感因素秦躯,v-on事件監(jiān)聽(tīng)器在DOM模板中實(shí)質(zhì)上會(huì)被自動(dòng)轉(zhuǎn)換為全小寫(xiě)忆谓,如此一來(lái),原本計(jì)劃通過(guò)駝峰式轉(zhuǎn)換成的短線(xiàn)式的監(jiān)聽(tīng)事件名也不可能被觸發(fā)了踱承,所以如果$emit使用駝峰式命名規(guī)則那么你的監(jiān)聽(tīng)事件也需要駝峰式命名倡缠。
? Vue建議使用短線(xiàn)式或全小寫(xiě)哨免,特別是前者

this.$emit('my-event', params)  // 發(fā)起
<my-component v-on:my-event="handleEmit"></my-component>  // 接收

?

# 動(dòng)態(tài)組件

? 比如我們有一個(gè)tab欄,其中有三個(gè)tab頁(yè)昙沦,點(diǎn)擊不同tab頁(yè)需切換至不同的組件下琢唾,此時(shí)非常適合使用is來(lái)指定不同的組件達(dá)到動(dòng)態(tài)組件效果,如下盾饮。 完整示例

<component :is="currentTabComponent"></component>

? 通過(guò)切換不同的tab能夠?qū)崿F(xiàn)不同組件的渲染采桃。注意當(dāng)你每次切換新標(biāo)簽的時(shí)候,Vue都創(chuàng)建了一個(gè)新的currentTabComponent實(shí)例丘损,因此普办,他不會(huì)保留切換前用戶(hù)停留的那個(gè)頁(yè)面狀態(tài)。通常來(lái)說(shuō)徘钥,重新創(chuàng)建實(shí)例的行為是符合預(yù)期的衔蹲,但也會(huì)有需要保留狀態(tài)的時(shí)候,就像是緩存下來(lái)一般

>> 使用keep-alive保留狀態(tài)
// 注意使用了keep-alive的組件必須要有name屬性
<keep-alive>
  <component :is="currentTabComponent"></component>
</keep-alive>

?

# 基礎(chǔ)組件的自動(dòng)化全局注冊(cè)

? 經(jīng)常為了美化頁(yè)面效果呈础,我們會(huì)對(duì)HTML元素做一層封裝舆驶,成為基礎(chǔ)組件,可能是一個(gè)輸入框而钞、一個(gè)按鈕又或者是別的沙廉。對(duì)于這些組件,Vue建議使用具有語(yǔ)義化的規(guī)范命名風(fēng)格笨忌,如以Base開(kāi)頭蓝仲,BaseButtonBaseIcon, BaseInput等官疲。引入這些組件往往占據(jù)了大量代碼空間
import好幾行袱结,components: {}又有好幾行,但是他們又只是模板中的很小的一部分途凫。
? 在 Vue CLI 3+ 中提供了require.context 通過(guò)全局注冊(cè)這些非常通用的基礎(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(
  // 其組件目錄的相對(duì)路徑
  './components',
  // 是否查詢(xún)其子目錄
  false,
  // 匹配基礎(chǔ)組件文件名的正則表達(dá)式
  /Base[A-Z]\w+\.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  // 獲取組件配置
  const componentConfig = requireComponent(fileName)
  // 獲取組件的 PascalCase 命名
  const componentName = upperFirst(
    camelCase(
      // 剝?nèi)ノ募_(kāi)頭的 `./` 和結(jié)尾的擴(kuò)展名
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    )
  )

  // 全局注冊(cè)組件
  Vue.component(
    componentName,
    // 如果這個(gè)組件選項(xiàng)是通過(guò) `export default` 導(dǎo)出的,
    // 那么就會(huì)優(yōu)先使用 `.default`维费,否則回退到使用模塊的根果元。
    componentConfig.default || componentConfig
  )
})

? 這里有一個(gè)真實(shí)案例
?

# Prop傳遞數(shù)據(jù)防臟

? 所有的 prop都使得其父子組件形成一個(gè)單向下行綁定,父級(jí)prop的更新會(huì)流動(dòng)到子組件中犀盟,但反過(guò)來(lái)不行而晒。這種設(shè)計(jì)辦法是為了防止子組件意外改變父組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解阅畴。另外倡怎,如果該數(shù)據(jù)還被其他子組件使用,也將受影響,產(chǎn)生泛洪式災(zāi)難监署。因此不應(yīng)該在子組件中設(shè)計(jì)修改prop數(shù)據(jù)的操作颤专。

在Javascript中對(duì)象和數(shù)組都是通過(guò)引用傳入的,因此對(duì)于引用類(lèi)型的prop來(lái)說(shuō)钠乏,在子組件中修改數(shù)據(jù)本身將直接改變父級(jí)的數(shù)據(jù)栖秕。

? 常見(jiàn)的試圖改變prop的操作有一下兩種情形:

  1. 接收的prop作為一個(gè)初始值,這個(gè)子組件接下來(lái)希望將其作為一個(gè)本地的prop數(shù)據(jù)來(lái)使用晓避。這種情況下應(yīng)該使用子組件中的data來(lái)拷貝一份prop數(shù)據(jù)數(shù)據(jù)
prop: [ 'initialNum' ],
data () {
  return {
    num: this.initialNum
  }
}
  1. 接收的prop作為原始的值需要進(jìn)行格式轉(zhuǎn)換簇捍。這種情況下,應(yīng)該使用計(jì)算屬性來(lái)實(shí)現(xiàn)
props: ['size'],
computed: {
  normalizedSize () {
    return this.size.trim().toLowerCase()
  }
}

? 當(dāng)不需要對(duì)prop做改變只是進(jìn)行使用時(shí)可以不用data拷貝够滑,但也需要注意使用垦写,曾經(jīng)遇到將 == 寫(xiě)成 =,花了不少時(shí)間找bug彰触。當(dāng)系統(tǒng)比較龐大時(shí)這種問(wèn)題不好找,所以大家一定要細(xì)心實(shí)在不行就多做個(gè)data拷貝命辖。
?

# prop自定義檢查函數(shù)

? Vue允許在進(jìn)行prop傳值時(shí)對(duì)值進(jìn)行驗(yàn)證况毅,type可以驗(yàn)證數(shù)據(jù)類(lèi)型,default可以設(shè)置當(dāng)未傳入時(shí)的默認(rèn)值尔艇。除此之外尔许,還允許開(kāi)發(fā)者們自定義驗(yàn)證函數(shù)

function CheckName (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

? 驗(yàn)證辦法如下

prop: {
  userName: CheckName
}

?

# Prop與自身屬性重名問(wèn)題

? 當(dāng)使用ElementUIBootstrap這些第三方插件時(shí),往往他們定義有自己的屬性终娃,如果開(kāi)發(fā)者們自定義的prop屬性與其發(fā)生重名時(shí)味廊,Vue在大多數(shù)情況下,從外部提供給組件的值會(huì)替換掉組件內(nèi)部設(shè)置好的值棠耕。

? 即假設(shè)存在傳入type="text" 就會(huì)替換掉本身type="date" 類(lèi)型余佛,原來(lái)的就會(huì)被破壞。慶幸的是窍荧, classstyle會(huì)智能一些辉巡,即兩邊的值會(huì)合并起來(lái)
?

# Prop實(shí)現(xiàn)‘雙向綁定’效果 .sync

? 父組件中的某個(gè)屬性值需要根據(jù)自身利用prop傳遞給子組件,然后在子組件中做一些操作后響應(yīng)回給父組件來(lái)更新這樣的需求時(shí)蕊退,除了使用$emitAPI郊楣,不妨試試.sync
? 作為一種語(yǔ)法糖存在,.sync修飾在v-bind上瓤荔,可以替代prop傳遞數(shù)據(jù)時(shí)v-on:updata:title="titleName"這種寫(xiě)法净蚤,(給屬性增加update:是Vue在這種需求下的推薦用法),后者還需要使用$emit來(lái)回傳值this.$emit('update:title', newTitle)输硝。.sync則顯得更加簡(jiǎn)便
? 需要注意的是今瀑,.sync修飾的屬性不能和表達(dá)是一起使用,如doc.title + "!"

<text-document v-bind:title.sync="doc.title"></text-document

?

# 將原生事件綁定到組件上

? 通常都是在原生的標(biāo)簽上使用事件的綁定,但有時(shí)候放椰,你可能想要在一個(gè)組件的根元素上直接監(jiān)聽(tīng)一個(gè)原生事件作烟,如使用CubeUI(一般UI庫(kù)自身會(huì)提供原生的繼承方法)或自定義組件上。這時(shí)砾医,你可以使用 v-on.native 修飾符:

<tab-item @click.native=""></tab-item>

?

# Vue 插槽

? Vue插槽非常重要拿撩,筆者為其特意編寫(xiě)了一個(gè)專(zhuān)題,詳情閱讀Vue插槽如蚜,高復(fù)用組件
?

# $ref

? 有時(shí)候需要直接訪(fǎng)問(wèn)一個(gè)子組件或子元素压恒,此時(shí)可以為他賦予一個(gè)ref作為唯一標(biāo)識(shí),通過(guò)$refs來(lái)訪(fǎng)問(wèn)

<self-input ref="nameInput"></self-input>

訪(fǎng)問(wèn)時(shí)使用 this.$refs.nameInput错邦,這樣就可以自由訪(fǎng)問(wèn)其內(nèi)部數(shù)據(jù)和方法了探赫。這種辦法同樣適用于元素上。

<input ref="innerInput"></input>

比如我們想在父組件中控制子組件中的input框自動(dòng)獲取焦點(diǎn)撬呢,可以這么做

method: {
  focus: function() {
    this.$refs.innerInput.focus()
  }
}

當(dāng)refv-ror一起使用時(shí)伦吠,得到的結(jié)果是包含了對(duì)應(yīng)數(shù)據(jù)源的這些子組件的數(shù)組。另外魂拦,需要注意的是毛仪,$refs只會(huì)在組件渲染完成之后生效,并且不是響應(yīng)式的芯勘。它并不適用與計(jì)算屬性

?

# $root & $parent

? 在每個(gè)vue實(shí)例中箱靴,提供了根實(shí)例和父實(shí)例的數(shù)據(jù)和方法,這只是一種訪(fǎng)問(wèn)數(shù)據(jù)的實(shí)例荷愕,對(duì)于小型應(yīng)用來(lái)說(shuō)很方便衡怀,跟建議使用Vuex的狀態(tài)管理機(jī)制。

  • $root 訪(fǎng)問(wèn)根實(shí)例的數(shù)據(jù)和方法安疗,包括計(jì)算屬性等
  • $parent 訪(fǎng)問(wèn)父實(shí)例的數(shù)據(jù)和方法抛杨,包括計(jì)算屬性等。修改父組件容易導(dǎo)致難以查找數(shù)據(jù)變更源
    ?

# 依賴(lài)注入provide & inject

? $root$parent只能實(shí)現(xiàn)根級(jí)實(shí)例訪(fǎng)問(wèn)和父級(jí)實(shí)例訪(fǎng)問(wèn)茂契,然而對(duì)于跨級(jí)的組件間數(shù)據(jù)交互蝶桶,雖然可以通過(guò)$parent一層層傳遞,但這不是一個(gè)好辦法掉冶。依賴(lài)注入有了用武之地真竖,通過(guò)兩個(gè)新的選項(xiàng):provideinject

? provide允許我們?cè)诋?dāng)前組件中指定想要提供給后代組件的數(shù)據(jù)和方法厌小,表現(xiàn)形式很像data選項(xiàng)

provide () {
  return {
    getMsg: this.getMsg
  }
}

它就像是一個(gè)大范圍的prop恢共,后代組件都可以使用inject選項(xiàng)倆注入它。

inject: [ 'getMsg' ]

通過(guò)依賴(lài)注入的數(shù)據(jù)也是非響應(yīng)式的璧亚,同樣不適用與計(jì)算屬性

?

# 后語(yǔ)

? 本文內(nèi)容大部分來(lái)自官網(wǎng)讨韭,作為提煉和融入筆者的一點(diǎn)思考,如有不對(duì)和不理解的地方歡迎與筆者交流和提出質(zhì)疑

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市透硝,隨后出現(xiàn)的幾起案子狰闪,更是在濱河造成了極大的恐慌,老刑警劉巖濒生,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埋泵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡罪治,警方通過(guò)查閱死者的電腦和手機(jī)丽声,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)觉义,“玉大人雁社,你說(shuō)我怎么就攤上這事∩购В” “怎么了霉撵?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)厉碟。 經(jīng)常有香客問(wèn)我喊巍,道長(zhǎng),這世上最難降的妖魔是什么箍鼓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮呵曹,結(jié)果婚禮上款咖,老公的妹妹穿的比我還像新娘。我一直安慰自己奄喂,他們只是感情好铐殃,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著跨新,像睡著了一般富腊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上域帐,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天赘被,我揣著相機(jī)與錄音,去河邊找鬼肖揣。 笑死民假,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的龙优。 我是一名探鬼主播羊异,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了野舶?” 一聲冷哼從身側(cè)響起易迹,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎平道,沒(méi)想到半個(gè)月后睹欲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巢掺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年句伶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陆淀。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡考余,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出轧苫,到底是詐尸還是另有隱情楚堤,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布含懊,位于F島的核電站身冬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岔乔。R本人自食惡果不足惜酥筝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雏门。 院中可真熱鬧嘿歌,春花似錦、人聲如沸茁影。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)募闲。三九已至步脓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浩螺,已是汗流浹背靴患。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留年扩,地道東北人蚁廓。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像厨幻,于是被迫代替她去往敵國(guó)和親相嵌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子腿时,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容