本文整理來(lái)自深入Vue3+TypeScript技術(shù)棧-coderwhy大神新課,只作為個(gè)人筆記記錄使用空繁,請(qǐng)大家多支持王紅元老師柳爽。
模板語(yǔ)法
React的開(kāi)發(fā)模式:
React使用的jsx,所以對(duì)應(yīng)的代碼都是編寫(xiě)的類似于js的一種語(yǔ)法犯助,之后通過(guò)Babel將jsx編譯成 React.createElement 函數(shù)調(diào)用。Vue也支持jsx的開(kāi)發(fā)模式(后續(xù)有時(shí)間也會(huì)講到):
但是大多數(shù)情況下优质,使用基于HTML的模板語(yǔ)法牲证,在模板中,允許開(kāi)發(fā)者以聲明式的方式將DOM和底層組件實(shí)例的數(shù)據(jù)綁定在一起锤窑。
在底層的實(shí)現(xiàn)中璧针,Vue將模板編譯成虛擬DOM渲染函數(shù),這個(gè)我會(huì)在后續(xù)給大家講到渊啰。所以探橱,對(duì)于學(xué)習(xí)Vue來(lái)說(shuō),學(xué)習(xí)模板語(yǔ)法是非常重要的绘证。
Mustache雙大括號(hào)語(yǔ)法
如果我們希望把數(shù)據(jù)顯示到模板(template)中隧膏,使用最多的語(yǔ)法是 “Mustache”語(yǔ)法 (雙大括號(hào)) 的文本插值。
并且我們前端提到過(guò)嚷那,data返回的對(duì)象是有添加到Vue的響應(yīng)式系統(tǒng) 中胞枕,當(dāng)data中的數(shù)據(jù)發(fā)生改變時(shí),對(duì)應(yīng)的內(nèi)容也會(huì)發(fā)生更新魏宽。
當(dāng)然腐泻,Mustache中不僅僅可以是data中的屬性,也可以是一個(gè)JavaScript的表達(dá)式:
<template id="my-app">
<!-- 1.mustache的基本使用 -->
<h2>{{message}} - {{message}}</h2>
<!-- 2.是一個(gè)表達(dá)式 -->
<h2>{{counter * 10}}</h2>
<h2>{{ message.split(" ").reverse().join(" ") }}</h2>
<!-- 3.也可以調(diào)用函數(shù) -->
<!-- 可以使用computed(計(jì)算屬性) -->
<h2>{{getReverseMessage()}}</h2>
<!-- 4.三元運(yùn)算符 -->
<h2>{{ isShow ? "哈哈哈": "" }}</h2>
</template>
下面這種寫(xiě)法是語(yǔ)句不是表達(dá)式队询,所以是錯(cuò)誤的:
v-once指令
v-once用于指定元素或者組件只渲染一次派桩,當(dāng)數(shù)據(jù)發(fā)生變化時(shí),元素或者組件以及其所有的子元素將視為靜態(tài)內(nèi)容并且跳過(guò)娘摔,該指令可以用于性能優(yōu)化窄坦。
如果添加到父節(jié)點(diǎn),那么所有的子節(jié)點(diǎn)也是只會(huì)渲染一次:
v-text指令
用于更新元素的 textContent凳寺,等價(jià)于"Mustache"語(yǔ)法鸭津,而且"Mustache"語(yǔ)法更靈活。
v-html
默認(rèn)情況下肠缨,如果我們展示的內(nèi)容本身是 html 的逆趋,那么vue并不會(huì)對(duì)其進(jìn)行特殊的解析。如果我們希望這個(gè)內(nèi)容被Vue可以解析出來(lái)晒奕,那么可以使用 v-html 來(lái)展示闻书。
<template id="my-app">
<div>{{msg}}</div>
<div v-html="msg"></div>
</template>
<script src="../js/vue.js"></script>
<script>
const App = {
template: '#my-app',
data() {
return {
msg: '<span style="color:red; background: blue;">哈哈哈</span>'
}
}
}
效果如下:
v-pre
v-pre用于跳過(guò)元素和它的子元素的編譯過(guò)程,顯示原始的Mustache標(biāo)簽脑慧。
跳過(guò)不需要編譯的節(jié)點(diǎn)魄眉,加快編譯的速度。
<template id="my-app">
<h2 v-pre>{{message}}</h2>
</template>
效果如下:
v-cloak
這個(gè)指令保持在元素上直到關(guān)聯(lián)組件實(shí)例結(jié)束編譯闷袒。
v-cloak 和 CSS 規(guī)則如 [v-cloak] { display: none } 一起用時(shí)坑律,這個(gè)指令可以隱藏未編譯的 Mustache 標(biāo)簽直到組件實(shí)例準(zhǔn)備完畢,主要用于解決閃動(dòng)問(wèn)題囊骤,現(xiàn)在Vue3一般不會(huì)出現(xiàn)這個(gè)問(wèn)題了晃择。
<div> 不會(huì)顯示冀值,直到編譯結(jié)束。
v-bind的綁定屬性
前面講的一系列指令宫屠,主要是將值插入到模板內(nèi)容中列疗。但是,除了內(nèi)容需要?jiǎng)討B(tài)來(lái)決定外浪蹂,某些屬性我們也希望動(dòng)態(tài)來(lái)綁定抵栈。比如動(dòng)態(tài)綁定a元素的href屬性,動(dòng)態(tài)綁定img元素的src屬性乌逐。
綁定屬性我們使用v-bind:
竭讳,縮寫(xiě):
,用于動(dòng)態(tài)地綁定一個(gè)或多個(gè) attribute浙踢,或一個(gè)組件 prop 到表達(dá)式。
綁定屬性我們使用v-bind:
縮寫(xiě):":"
預(yù)期:any (with argument) | Object (without argument)
參數(shù):attrOrProp (optional)
修飾符:
??.camel - 將 kebab-case attribute 名轉(zhuǎn)換為 camelCase
用法:動(dòng)態(tài)地綁定一個(gè)或多個(gè) attribute灿渴,或一個(gè)組件 prop 到表達(dá)式
1. 綁定基本屬性
v-bind用于綁定一個(gè)或多個(gè)屬性值洛波,或者向另一個(gè)組件傳遞props值(這個(gè)學(xué)到組件時(shí)再介紹),在開(kāi)發(fā)中骚露,有哪些屬性需要?jiǎng)討B(tài)進(jìn)行綁定呢蹬挤?還是有很多的,比如圖片的鏈接src棘幸、網(wǎng)站的鏈接href焰扳、動(dòng)態(tài)綁定一些類、樣式等等误续。
v-bind有一個(gè)對(duì)應(yīng)的語(yǔ)法糖吨悍,也就是簡(jiǎn)寫(xiě)方式,在開(kāi)發(fā)中蹋嵌,我們通常會(huì)使用語(yǔ)法糖的形式育瓜,因?yàn)楦?jiǎn)潔。
注意:Vue2 template模板中只能有一個(gè)根元素栽烂,Vue3 template模板中允許有多個(gè)根元素躏仇。
2. 綁定class
在開(kāi)發(fā)中,有時(shí)候我們的元素class也是動(dòng)態(tài)的腺办,比如:當(dāng)數(shù)據(jù)為某個(gè)狀態(tài)時(shí)焰手,字體顯示紅色,當(dāng)數(shù)據(jù)另一個(gè)狀態(tài)時(shí)怀喉,字體顯示黑色书妻。
綁定class有兩種方式:對(duì)象語(yǔ)法,數(shù)組語(yǔ)法磺送。
① 對(duì)象語(yǔ)法:我們可以傳給 :class (v-bind:class 的簡(jiǎn)寫(xiě)) 一個(gè)對(duì)象驻子,以動(dòng)態(tài)地切換 class灿意。
② 數(shù)組語(yǔ)法:我們可以把一個(gè)數(shù)組傳給 :class,以應(yīng)用一個(gè) class 列表崇呵;
3. 綁定style
我們可以利用v-bind:style來(lái)綁定一些CSS內(nèi)聯(lián)樣式缤剧,這是因?yàn)槟承邮轿覀冃枰鶕?jù)數(shù)據(jù)動(dòng)態(tài)來(lái)決定,比如某段文字的顏色域慷,大小等等荒辕。
CSS屬性名可以用駝峰式 (camelCase) 或短橫線分隔 (kebab-case,記得用引號(hào)括起來(lái)) 來(lái)命名犹褒。
綁定style有兩種方式:對(duì)象語(yǔ)法抵窒,數(shù)組語(yǔ)法。
① 對(duì)象語(yǔ)法:
② 數(shù)組語(yǔ)法::style
的數(shù)組語(yǔ)法可以將多個(gè)樣式對(duì)象應(yīng)用到同一個(gè)元素上
style1Obj1: {
color: 'red',
fontSize: '30px'
},
4. 動(dòng)態(tài)綁定屬性
在某些情況下叠骑,我們屬性的名稱可能也不是固定的李皇。
前面我們無(wú)論綁定src、href宙枷、class掉房、style,屬性名稱都是固定的慰丛,如果屬性名稱不是固定的卓囚,我們可以使用 :[屬性名]=“值” 的格式來(lái)定義,這種綁定的方式诅病,我們稱之為動(dòng)態(tài)綁定屬性哪亿。
data() {
return {
name: "cba",
value: "kobe"
}
}
5. 綁定一個(gè)對(duì)象
如果我們希望將一個(gè)對(duì)象的所有屬性,綁定到元素上的所有屬性贤笆,應(yīng)該怎么做呢蝇棉?非常簡(jiǎn)單,我們可以直接使用 v-bind 綁定一個(gè)對(duì)象苏潜。
如下:info對(duì)象會(huì)被拆解成div的各個(gè)屬性银萍。
data() {
return {
info: {
name: "why",
age: 18,
height: 1.88
}
}
}
v-on綁定事件
前面我們綁定了元素的內(nèi)容和屬性,在前端開(kāi)發(fā)中另外一個(gè)非常重要的特性就是交互恤左。
在前端開(kāi)發(fā)中贴唇,我們需要經(jīng)常和用戶進(jìn)行各種各樣的交互,這個(gè)時(shí)候飞袋,我們就必須監(jiān)聽(tīng)用戶發(fā)生的事件戳气,比如點(diǎn)擊、拖拽巧鸭、鍵盤事件等等瓶您。
在Vue中如何監(jiān)聽(tīng)事件呢?使用v-on指令。接下來(lái)我們來(lái)看一下v-on的用法:
v-on的使用:
縮寫(xiě):@
預(yù)期:Function | Inline Statement | Object
參數(shù):event
修飾符:
??.stop - 調(diào)用 event.stopPropagation()
??.prevent - 調(diào)用 event.preventDefault()
??.capture - 添加事件偵聽(tīng)器時(shí)使用 capture 模式
??.self - 只當(dāng)事件是從偵聽(tīng)器綁定的元素本身觸發(fā)時(shí)才觸發(fā)回調(diào)
??.{keyAlias} - 僅當(dāng)事件是從特定鍵觸發(fā)時(shí)才觸發(fā)回調(diào)
??.once - 只觸發(fā)一次回調(diào)
??.left - 只當(dāng)點(diǎn)擊鼠標(biāo)左鍵時(shí)觸發(fā)
??.right - 只當(dāng)點(diǎn)擊鼠標(biāo)右鍵時(shí)觸發(fā)
??.middle - 只當(dāng)點(diǎn)擊鼠標(biāo)中鍵時(shí)觸發(fā)
??.passive - { passive: true } 模式添加偵聽(tīng)器
用法:綁定事件監(jiān)聽(tīng)
1. v-on的基本使用
我們可以使用v-on來(lái)監(jiān)聽(tīng)一下點(diǎn)擊的事件:
v-on:click可以寫(xiě)成@click呀袱,是它的語(yǔ)法糖寫(xiě)法:
當(dāng)然贸毕,我們也可以綁定其他的事件:
如果我們希望一個(gè)元素綁定多個(gè)事件,這個(gè)時(shí)候可以傳入一個(gè)對(duì)象:
2. v-on參數(shù)傳遞
當(dāng)通過(guò)methods中定義方法夜赵,以供@click調(diào)用時(shí)明棍,需要注意參數(shù)問(wèn)題:
情況一:如果該方法不需要額外參數(shù),那么方法后的()可以不添加寇僧,并且方法的實(shí)現(xiàn)不用參數(shù)摊腋,直接就可以打印event。
情況二:如果需要同時(shí)傳入某個(gè)參數(shù)和event時(shí)嘁傀,可以通過(guò)$event傳入事件兴蒸,并且方法的實(shí)現(xiàn)必須按順序?qū)懨鲄?shù)。
3. v-on的修飾符
v-on支持修飾符细办,修飾符相當(dāng)于對(duì)事件進(jìn)行了一些特殊的處理:
??.stop - 調(diào)用 event.stopPropagation()
??.prevent - 調(diào)用 event.preventDefault()
??.capture - 添加事件偵聽(tīng)器時(shí)使用 capture 模式
??.self - 只當(dāng)事件是從偵聽(tīng)器綁定的元素本身觸發(fā)時(shí)才觸發(fā)回調(diào)
??.{keyAlias} - 僅當(dāng)事件是從特定鍵觸發(fā)時(shí)才觸發(fā)回調(diào)
??.once - 只觸發(fā)一次回調(diào)
??.left - 只當(dāng)點(diǎn)擊鼠標(biāo)左鍵時(shí)觸發(fā)
??.right - 只當(dāng)點(diǎn)擊鼠標(biāo)右鍵時(shí)觸發(fā)
??.middle - 只當(dāng)點(diǎn)擊鼠標(biāo)中鍵時(shí)觸發(fā)
??.passive - { passive: true } 模式添加偵聽(tīng)器
@keyup.enter 代表enter鍵彈起的時(shí)候會(huì)調(diào)用onEnter方法橙凳,我們一般在方法里面獲取輸入的值:
onEnter() {
console.log(event.target.value);
}
v-if 條件渲染
在某些情況下,我們需要根據(jù)當(dāng)前的條件決定某些元素或組件是否渲染蟹腾,這個(gè)時(shí)候我們就需要進(jìn)行條件判斷了痕惋。
Vue提供了下面的指令來(lái)進(jìn)行條件判斷:
v-if
v-else
v-else-if
v-show
下面我們來(lái)對(duì)它們進(jìn)行學(xué)習(xí)。
1. v-if娃殖、v-else、v-else-if
v-if议谷、v-else炉爆、v-else-if 用于根據(jù)條件來(lái)渲染某一塊的內(nèi)容,這些內(nèi)容只有在條件為true時(shí)卧晓,才會(huì)被渲染出來(lái)芬首,這三個(gè)指令與JavaScript的條件語(yǔ)句 if、else逼裆、else if 類似郁稍。
v-if 的渲染原理:v-if是惰性的,當(dāng)條件為false時(shí)胜宇,其判斷的內(nèi)容完全不會(huì)被渲染或者會(huì)被銷毀掉耀怜,當(dāng)條件為true時(shí),才會(huì)真正渲染條件塊中的內(nèi)容桐愉。
2. template元素
因?yàn)関-if是一個(gè)指令财破,所以必須將其添加到一個(gè)元素上,但是如果我們希望切換的是多個(gè)元素呢从诲?
如果此時(shí)我們使用div包裹左痢,div會(huì)被渲染到界面上來(lái),但是我們并不希望div被渲染,這個(gè)時(shí)候俊性,我們可以選擇使用template略步,template元素可以當(dāng)做不可見(jiàn)的包裹元素,并且 v-if 可以添加到 template 上定页,但是最終template不會(huì)被渲染出來(lái)趟薄,類似于小程序中的block。
3. v-show
v-show和v-if的用法看起來(lái)是一致的拯勉,也是根據(jù)一個(gè)條件決定是否顯示元素或者組件竟趾。
4. v-show和v-if的區(qū)別
首先,在用法上的區(qū)別:
- v-show是不支持template
- v-show不可以和v-else一起使用
其次宫峦,本質(zhì)的區(qū)別:
- v-show元素?zé)o論是否需要顯示到瀏覽器上岔帽,它的DOM實(shí)際都是有渲染的,只是通過(guò)CSS的display屬性來(lái)進(jìn)行切換导绷,所以v-show是不支持template
- v-if當(dāng)條件為false時(shí)犀勒,其對(duì)應(yīng)的原生壓根不會(huì)被渲染到DOM中
開(kāi)發(fā)中如何進(jìn)行選擇呢?
- 如果我們的原生需要在顯示和隱藏之間頻繁的切換妥曲,那么使用v-show
- 如果不會(huì)頻繁的發(fā)生切換贾费,那么使用v-if
v-for列表渲染
在真實(shí)開(kāi)發(fā)中,我們往往會(huì)從服務(wù)器拿到一組數(shù)據(jù)檐盟,并且需要對(duì)其進(jìn)行渲染褂萧。這個(gè)時(shí)候我們可以使用v-for來(lái)完成,v-for類似于JavaScript的for循環(huán)葵萎,可以用于遍歷一組數(shù)據(jù)导犹。
v-for基本使用
v-for的基本格式是"item in 數(shù)組"
,數(shù)組通常是來(lái)自data或者prop羡忘,也可以是其他方式谎痢,item是我們給每項(xiàng)元素起的一個(gè)別名,這個(gè)別名可以自定來(lái)定義卷雕。
我們知道节猿,在遍歷一個(gè)數(shù)組的時(shí)候會(huì)經(jīng)常需要拿到數(shù)組的索引,如果我們需要索引漫雕,可以使用格式"(item, index) in 數(shù)組"
滨嘱,注意順序,數(shù)組元素項(xiàng)item在前面蝎亚,索引項(xiàng)index在后面九孩。
v-for支持的類型
v-for也支持遍歷對(duì)象,并且支持有一二三個(gè)參數(shù):
一個(gè)參數(shù):"value in object"
二個(gè)參數(shù):"(value, key) in object"
三個(gè)參數(shù):"(value, key, index) in object"v-for同時(shí)也支持?jǐn)?shù)字的遍歷:
每一個(gè)item都是一個(gè)數(shù)字发框,從1開(kāi)始躺彬。
template元素
類似于v-if煤墙,你可以使用 template 元素來(lái)循環(huán)渲染一段包含多個(gè)元素的內(nèi)容。
我們使用template來(lái)對(duì)多個(gè)元素進(jìn)行包裹宪拥,而不是使用div來(lái)完成仿野,因?yàn)閐iv會(huì)被渲染,template不會(huì)被渲染她君。而且如果有ul脚作,ul里面不推薦放div,只推薦放li缔刹。
數(shù)組更新檢測(cè)
Vue 將被偵聽(tīng)的數(shù)組的變更方法進(jìn)行了包裹球涛,所以它們也將會(huì)觸發(fā)視圖更新,這些被包裹過(guò)的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
上面的方法會(huì)直接修改原來(lái)的數(shù)組校镐,所以視圖會(huì)跟著更新亿扁。但是某些方法不會(huì)替換原來(lái)的數(shù)組,而是會(huì)生成新的數(shù)組鸟廓,比如 filter()从祝、concat() 和 slice(),這時(shí)候我們可以通過(guò)重新賦值的方式觸發(fā)視圖更新引谜,如下:
this.movies = this.movies.filter(item => item.length > 2);
v-for中的key是什么作用牍陌?
在使用v-for進(jìn)行列表渲染時(shí),我們通常會(huì)給元素或者組件綁定一個(gè)key屬性员咽。
這個(gè)key屬性有什么作用呢毒涧?
我們先來(lái)看一下官方的解釋:key屬性主要用在Vue的虛擬DOM算法,在新舊nodes對(duì)比時(shí)辨識(shí)VNodes贝室。如果不使用key链嘀,Vue會(huì)使用一種最大限度減少動(dòng)態(tài)元素并且盡可能的嘗試就地修改/復(fù)用相同類型元素的算法,而使用key時(shí)档玻,它會(huì)基于key的變化重新排列元素順序,并且會(huì)移除/銷毀key不存在的元素茫藏。
官方的解釋對(duì)于初學(xué)者來(lái)說(shuō)并不好理解误趴,比如下面的問(wèn)題:
什么是新舊nodes,什么是VNode务傲?
沒(méi)有key的時(shí)候凉当,如何嘗試修改和復(fù)用的?
有key的時(shí)候售葡,如何基于key重新排列的看杭?
認(rèn)識(shí)VNode
我們先來(lái)解釋一下VNode的概念:
VNode的全稱是Virtual Node,也就是虛擬節(jié)點(diǎn)挟伙。事實(shí)上楼雹,無(wú)論是組件還是元素,它們最終在Vue中表示出來(lái)的都是一個(gè)個(gè)VNode。VNode的本質(zhì)是一個(gè)JavaScript的對(duì)象贮缅。
虛擬DOM
如果我們不只是一個(gè)簡(jiǎn)單的div榨咐,而是有一大堆的元素,那么它們應(yīng)該會(huì)形成一個(gè)VNode Tree谴供。
插入F的案例
我們先來(lái)看一個(gè)案例:這個(gè)案例是當(dāng)我們點(diǎn)擊按鈕時(shí)會(huì)在li中間插入一個(gè)f块茁。
我們可以確定的是,這次更新對(duì)于ul和button是不需要進(jìn)行更新桂肌,需要更新的是我們li的列表数焊。在Vue中,對(duì)于相同父元素的子元素節(jié)點(diǎn)并不會(huì)重新渲染整個(gè)列表崎场,因?yàn)閷?duì)于列表中 a佩耳、b、c照雁、d它們都是沒(méi)有變化的蚕愤。在操作真實(shí)DOM的時(shí)候,我們只需要在中間插入一個(gè)f的li即可饺蚊。
那么Vue中對(duì)于列表的更新究竟是如何操作的呢萍诱?
Vue事實(shí)上會(huì)對(duì)于有key和沒(méi)有key會(huì)調(diào)用兩個(gè)不同的方法,有key污呼,那么就調(diào)用 patchKeyedChildren方法裕坊,沒(méi)有key,那么就調(diào)用 patchUnkeyedChildren方法燕酷。
Vue源碼對(duì)于key的判斷
沒(méi)有key的操作(源碼)
沒(méi)有key的diff算法:
我們會(huì)發(fā)現(xiàn)上面的diff算法效率并不高籍凝,c和d來(lái)說(shuō)它們事實(shí)上并不需要有任何的改動(dòng),但是因?yàn)槲覀兊腸被f所使用了苗缩,所有后續(xù)所有的內(nèi)容都要一次進(jìn)行改動(dòng)饵蒂,并且最后進(jìn)行新增。
有key的操作(源碼)
有key的diff算法:
- 第一步的操作是從頭開(kāi)始進(jìn)行遍歷酱讶、比較:
a和b是一致的會(huì)繼續(xù)進(jìn)行比較退盯。
c和f因?yàn)閗ey不一致,所以就會(huì)break跳出循環(huán)泻肯。
- 第二步的操作是從尾部開(kāi)始進(jìn)行遍歷渊迁、比較:
- 第三步是如果舊節(jié)點(diǎn)遍歷完畢,但是依然有新的節(jié)點(diǎn)灶挟,那么就新增節(jié)點(diǎn):
- 第四步是如果新的節(jié)點(diǎn)遍歷完畢琉朽,但是依然有舊的節(jié)點(diǎn),那么就移除舊節(jié)點(diǎn):
- 第五步是最特色的情況稚铣,中間還有很多未知的或者亂序的節(jié)點(diǎn):
所以我們可以發(fā)現(xiàn)箱叁,Vue在進(jìn)行diff算法的時(shí)候墅垮,會(huì)盡量利用我們的key來(lái)進(jìn)行優(yōu)化操作,在沒(méi)有key的時(shí)候我們的效率是非常低效的蝌蹂,在進(jìn)行插入或者重置順序的時(shí)候噩斟,保持相同的key可以讓diff算法更加的高效。