vue.js 循環(huán)渲染
[TOC]
一谣光、簡(jiǎn)介
vue.js 的循環(huán)渲染是依賴于 v-for
指令,它能夠根據(jù) vue 的實(shí)例里面的信息,循環(huán)遍歷所需數(shù)據(jù)野瘦,然后渲染出相應(yīng)的內(nèi)容。它可以遍歷數(shù)組類型以及對(duì)象類型的數(shù)據(jù)飒泻,js 里面的數(shù)組本身實(shí)質(zhì)上也是對(duì)象鞭光,這里遍歷數(shù)組和對(duì)象的時(shí)候,方式相似但又稍有不同泞遗。
(一)遍歷對(duì)象
<div id="app">
<ul>
<li v-for="(val, key, index) in me">{{index}}. {{key}}: {{val}}</li>
</ul>
</div>
...
var vm = new Vue({
el: '#app',
data: {
me: {
name: 'Dale',
age: 22,
sex: 'male',
height: 170
}
}
});
這里惰许,v-for
接收的參數(shù)相對(duì)較復(fù)雜,但是可以分為三個(gè)部分:
(1)括號(hào)及其內(nèi)的遍歷結(jié)果信息(val, key, index)
其中史辙,val 是遍歷得到的屬性值汹买,key 是遍歷得到的屬性名,index 是遍歷次序聊倔,這里的 key/index
都是可選參數(shù)晦毙,如果不需要,這個(gè)指令其實(shí)可以寫成 v-for="val in me"
耙蔑;
(2)遍歷關(guān)鍵詞 in
in
可以使用 of
替代见妒,官方的說(shuō)法是“它是最接近 JavaScript 迭代器的語(yǔ)法”,但其實(shí)使用上并沒(méi)有任何區(qū)別甸陌;
(3)被遍歷對(duì)象 me
me
是綁定在實(shí)例 data
屬性上的一個(gè)屬性须揣,實(shí)際上盐股,它是有一個(gè)執(zhí)行環(huán)境的,也即是我們接觸最多的 vue 實(shí)例返敬,模板中遂庄,我們?nèi)耘f可以像在 methods
以及計(jì)算屬性中一樣,通過(guò) this
訪問(wèn)到它劲赠,這里的 me
其實(shí)就相當(dāng)于 this.me
涛目,模板中直接寫 this.me
也是可行的。
渲染結(jié)果如下:
<div id="app">
<ul>
<li>0. name: Dale</li>
<li>1. age: 22</li>
<li>2. sex: male</li>
<li>3. height: 170</li>
</ul>
</div>
(二)遍歷數(shù)組
<div id="app">
<ul>
<li v-for="(item, index) in items">{{index}}. {{item}}</li>
</ul>
</div>
...
var vm = new Vue({
el: '#app',
data: {
items: ['apple', 'tomato', 'banana', 'watermelon']
}
});
和遍歷對(duì)象相類似凛澎,最大的不同點(diǎn)在于對(duì)象的 “key” 和 “index” 是一致的霹肝,所以這里我們只需要取一個(gè) index
即可,上面代碼的渲染結(jié)果如下:
<div id="app">
<ul>
<li>0. apple</li>
<li>1. tomato</li>
<li>2. banana</li>
<li>3. watermelon</li>
</ul>
</div>
(三)遍歷“整數(shù)”
理論上來(lái)說(shuō)塑煎,整數(shù)并不是一個(gè)可遍歷的單元沫换,但是 vue 這里相當(dāng)于給我們提供了一個(gè)方便方式來(lái)減少重復(fù)代碼。
<div id="app">
<ul>
<li v-for="n in num">{{n}}</li>
</ul>
</div>
...
var vm = new Vue({
el: '#app',
data: {
num: 3
}
});
渲染結(jié)果如下:
<div id="app">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
二最铁、實(shí)際應(yīng)用
(一)對(duì)象讯赏、數(shù)組 & 組件
實(shí)際應(yīng)用過(guò)程中,我們單獨(dú)使用數(shù)組或者對(duì)象去描述我們的數(shù)據(jù)的情況很少冷尉,更常見(jiàn)的模式是結(jié)合了數(shù)組和對(duì)象兩部分內(nèi)容:
<div id="app">
<ul class="persons">
<li v-for="person in persons">name: {{person.name}}, age: {{person.age}};</li>
</ul>
</div>
...
var vm = new Vue({
el: '#app',
data: {
persons: [
{
name: 'Dale',
age: 22
},
{
name: 'Tim',
age: 30
},
{
name: 'Rex',
age: 23
}
]
}
});
本質(zhì)上是遍歷的一個(gè)數(shù)組漱挎,但是數(shù)組的每個(gè)元素卻是一個(gè)對(duì)象,也就是上面的 person
雀哨,我們可以通過(guò) []
以及 .
兩種方式去訪問(wèn)這個(gè)對(duì)象的屬性磕谅,比如這里的 name
/ age
,渲染結(jié)果如下:
<div id="app">
<ul class="persons">
<li>name: Dale, age: 22;</li>
<li>name: Tim, age: 30;</li>
<li>name: Rex, age: 23;</li>
</ul>
</div>
實(shí)際上雾棺,更加常用且強(qiáng)大的模式膊夹,是使用組件與數(shù)組/對(duì)象進(jìn)行組合,操作方式與上面相類似捌浩。
(二)與 v-if
組合
添加了 v-for
指令的標(biāo)簽放刨,實(shí)際上也可以同時(shí)添加 v-if
指令,但值得注意的是尸饺,v-for
的優(yōu)先級(jí)更高宏榕,渲染模板時(shí),相當(dāng)于對(duì)每次遍歷的結(jié)果進(jìn)行了一次條件判斷侵佃。
<div id="app">
<ul class="persons">
<li v-for="person in persons" v-if="person.age >= 23">name: {{person.name}}, age: {{person.age}};</li>
</ul>
</div>
...
var vm = new Vue({
el: '#app',
data: {
persons: [
{
name: 'Dale',
age: 22
},
{
name: 'Tim',
age: 30
},
{
name: 'Rex',
age: 23
}
]
}
});
這里先遍歷了 persons
的所有元素,然后檢查每次得到的 person
的是否大于或等于 23奠支,是則輸出馋辈,否則不輸出,渲染結(jié)果如下:
<div id="app">
<ul class="persons">
<li>name: Tim, age: 30;</li>
<li>name: Rex, age: 23;</li>
</ul>
</div>
如果要讓 v-if
指令的優(yōu)先級(jí)更高倍谜,可以考慮在 v-for
指令所綁定的標(biāo)簽的父級(jí)上添加 v-if
指令迈螟。
三叉抡、注意事項(xiàng)
(一)key
與 v-for
一樣,在不綁定 key
屬性的情況下答毫,vue 默認(rèn)會(huì)重用元素以提高性能褥民,如果不需要它的默認(rèn)行為,顯式綁定一個(gè)唯一的 key
即可洗搂。
(二)數(shù)據(jù) -> 視圖更新
vue 的視圖更新是依賴于 getter/setter 的消返,如果直接修改、增加耘拇、刪除數(shù)組元素撵颊,并不會(huì)觸發(fā)視圖的更新。這里 vue 重寫了如下方法:
push
pop
shift
unshift
splice
sort
reverse
當(dāng)通過(guò)它們修改數(shù)據(jù)的時(shí)候惫叛,將會(huì)觸發(fā)視圖的更新倡勇。
new Vue({
data: {
arr: [1, 2, 3]
}
});
比如上面這種情況,如果我們想要在執(zhí)行 arr
的 push
等方法嘉涌,因?yàn)?push
是數(shù)組類型數(shù)據(jù)從 Array.prototype.push
繼承過(guò)來(lái)的妻熊,所以我們一般情況下有兩種實(shí)現(xiàn)方式。
(1)修改 Array.prototype
Array.prototype.push = function () {
console.log(1);
}
([]).push(); // 1
這里我們修改了 Array.prototype
上的 push
方法仑最,但是實(shí)際上扔役,整個(gè) prototype
屬性都可以被重寫,如 Array.prototype = xxx
词身,這樣做的好處很明顯厅目,在這一處進(jìn)行修改,之后所有的數(shù)組類型都可以直接使用這個(gè)重寫后的方法法严,實(shí)現(xiàn)和使用都非常簡(jiǎn)單损敷;但是這樣帶來(lái)的副作用也特別明顯,容易影響到其它的代碼深啤,其它使用這段代碼的地方拗馒,除了功能實(shí)現(xiàn)上可能受到影響外,效率上也會(huì)有較大影響溯街,原生 js 的代碼都是經(jīng)過(guò)特殊優(yōu)化的诱桂,我們重寫實(shí)現(xiàn),效率肯定會(huì)受到影響呈昔,最重要的是挥等,如果大家的代碼在同一個(gè)環(huán)境下運(yùn)行,然后都嘗試重寫同一個(gè)方法的話堤尾,最終結(jié)果不言而喻肝劲。
(2)增加自有方法
var arr = [];
arr.push = function () {
console.log(1);
}
arr.push(); // 1
Array.prototype.push.toString(); // "function push() { [native code] }"
這里修改了 arr
的 push
方法, 但是并不涉及 Array.prototype.push
,因?yàn)樽x寫一個(gè)對(duì)象的屬性/方法的時(shí)候辞槐,js 總是先嘗試訪問(wèn) “ownproperty”掷漱,也就是 “hasOwnProperty” 所檢測(cè)的內(nèi)容,這里我們姑且將其稱為“自有屬性(方法)”榄檬。讀取數(shù)據(jù)的時(shí)候卜范,如果沒(méi)有讀取到內(nèi)容,那么 js 會(huì)嘗試向上搜索 __proto__
上的數(shù)據(jù)鹿榜;寫數(shù)據(jù)的時(shí)候海雪,如果有這個(gè)自有屬性,則會(huì)將其覆蓋犬缨,如果沒(méi)有喳魏,則將其作為自有屬性添加到改對(duì)象上,而不會(huì)嘗試將其添加到 __proto__
上怀薛,這樣的規(guī)則刺彩,也是為了防止“子類”以外修改“父類”的屬性、方法等枝恋。這種實(shí)現(xiàn)方式雖然可以避免上面修改 Array.prototype
的一系列缺點(diǎn)创倔,但是它的問(wèn)題就更加明顯,因?yàn)槊看蝿?chuàng)建這樣一個(gè)“數(shù)組”焚碌,就要重新實(shí)現(xiàn)/綁定這樣一系列方法畦攘,它所帶來(lái)的開(kāi)發(fā)效率、性能問(wèn)題不容小覷十电。
(3)vue 的實(shí)現(xiàn)方式
var arr = [];
new Vue({
data: {
arr: arr
}
});
arr.push.toString(); // "function mutator() {var arguments$1 = arguments;... 這是 vue 自己的實(shí)現(xiàn)
Array.prototype.push.toString(); // "function push() { [native code] }"... 這是瀏覽器原生的實(shí)現(xiàn)
arr.hasOwnProperty('push'); // false 說(shuō)明不是自有屬性
以上說(shuō)明 vue 既不是修改了 Array.prototype.push
知押,又不是修改了自有屬性。但我們通過(guò) instanceof
操作符檢查的時(shí)候鹃骂,arr
又是 Array
的一個(gè)實(shí)例,那么它到底是怎么弄的實(shí)現(xiàn)的呢台盯?或者說(shuō) vue 的 push
藏在哪兒呢?
var base = [];
var arr = [];
base.push = function () {
console.log(1);
};
arr.__proto__ = base;
arr.push(); // 1
arr.__proto__.push(); // 1
arr.__proto__.push.toString(); // "function push() { [native code] }"
實(shí)際上畏线,vue 是利用了類似上面的方式静盅,先創(chuàng)建了一個(gè) Array
的實(shí)例,也就是一個(gè)數(shù)組類型的基礎(chǔ)對(duì)象 base
寝殴,然后為它添加了一個(gè)自有方法 push
蒿叠,最后用 base
覆蓋了需要擴(kuò)展的 arr
對(duì)象的 __proto__
屬性。
這里需要重寫
push
等方法的數(shù)組蚣常,我們只需要將其 __proto__
指向 base
數(shù)組市咽,在讀新創(chuàng)建的數(shù)組的 push
的時(shí)候,發(fā)現(xiàn)它并沒(méi)有這樣一個(gè)自有方法抵蚊,那么它就嘗試讀 __proto__
上的方法魂务,發(fā)現(xiàn) __proto__
屬性(也即 base
數(shù)組)上有這樣一個(gè)自有方法曼验,那么它就不必再向上搜索而直接使用 base.push
。通過(guò)這種方式粘姜,我們不必為每一個(gè)數(shù)組重寫一遍
push
方法,也不必去修改 Array.prototype
熔酷,看起來(lái)倒像是一個(gè)兩全其美的方法孤紧。