事情的緣由
在寫一個專案的時候希望使用Map來進行遍歷,然後我取出了Map的keys,keys是一個Map Iterator
(迭代器,ES6引入的Symbol.iterator
)型別,這時我嘗試使用v-for
來對迭代器進行遍歷,當然我的期望是能夠遍歷出來。
我們都知道vue的v-for
提供了兩種遍歷方式,
v-for="item in items"
v-for="item of items"
複製程式碼
而js中可以使用
for (index in items)
for (item of items) // 這種寫法支援對迭代器的遍歷
複製程式碼
這時我不禁就想,既然如此,v-for
當中of
的遍歷方式應該是支援迭代器的遍歷的,但是事實嘗試下來是不可以的,那麼為什麼不可以呢,然後就去翻查了一下Vue的原始碼,從中找到了這麼一段程式碼。
/**
* Runtime helper for rendering v-for lists.
*/
function renderList (
val,
render
) {
var ret, i, l, keys, key
if (Array.isArray(val) || typeof val === 'string') {
ret = new Array(val.length)
for (i = 0, l = val.length; i < l; i++) {
ret[i] = render(val[i], i)
}
} else if (typeof val === 'number') {
ret = new Array(val)
for (i = 0; i < val; i++) {
ret[i] = render(i + 1, i)
}
} else if (isObject(val)) {
keys = Object.keys(val)
ret = new Array(keys.length)
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i]
ret[i] = render(val[key], key, i)
}
}
if (isDef(ret)) {
(ret)._isVList = true
}
return ret
}
複製程式碼
我嘗試在該段程式碼中打上了斷點,然後發現迭代器本身是一個Object型別(ES5沒有Symbol),但是Map Iterator
的Object.keys(val)
則是一個空物件[]
,一個空的keys
,所以此處不會產生遍歷然後進行render
。
作死的開始
既然沒有判斷出是一個Iterator那麼我就新增一個判斷,由於剛開始以為不能使用ES5以上的語法,修修改改了好多次,寫出瞭如下的程式碼,其實這裡可以判斷型別是否為Symbol.iterator
...
} else if (isObject(val)) {
keys = Object.keys(val)
if (keys.length === 0 && val.toString().indexOf('Iterator') > -1) { // 可能是個迭代器
ret = []
i = 0
while (true) {
if (typeof val.next !== 'function') {
break
}
var next = val.next()
if (next.done) {
break
}
ret.push(render(next.value, i++))
}
} else {
ret = new Array(keys.length)
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i]
ret[i] = render(val[key], key, i)
}
}
}
...
複製程式碼
Symbol型別有個方法是Symbbol.toStringTag
Symol.toStringTag
A string value for the default description of an Object. Used by
Object.prototype.toString()
查閱資料看到了這麼一段話,那麼我就是用toString方法去取出迭代器的description然後使用indexOf去判斷是否包含Iterator來區分是不是一個迭代器。
這裡使用了while卻沒有使用for...of的寫法, 由於修改的是dist的檔案所以不敢嘗試ES5+的寫法。
然後一切都看起來很完美,但是執行的時候卻走到了handleError(e, vm, "render")
一個ERROR分支...百思不得其解...
經過了N輪的斷掉除錯,我終於定位了到問題的所在。
由於測試的時候是基於專案做的,專案用的ElementUI,其中用到了form元件,層層定位發現是v-model的值為空造成的Error,然後寫了一份簡單的v-for發現竟然真的可以了。
template...
<div v-for="item of iterator">
{{item}}
</div>
script...
data () {
return {
iterator: this.getIterator()
}
},
methods: {
getIterator () {
let map = new Map()
map.set('title', '')
map.set('value', '')
return map.keys()
}
}
output...
title
value
複製程式碼
總結
雖然我在Vue上做了一個ES5+的嘗試不知道合不合理,但是在這個探索的過程中學到了很多,希望能對大家有所幫助。
其中瞭解了Symbol型別究竟是個怎樣的型別,瞭解了迭代器的實現,實踐了斷點定位問題等等...一個晚上的付出沒白費,還是比較欣喜的。
End
I'm 一個渴望成長的碼農