讓Vue的v-for支持迭代器遍歷

事情的原因

在寫一個項目的時候但願使用Map來進行遍歷,而後我取出了Map的keys,keys是一個Map Iterator(迭代器,ES6引入的Symbol.iterator)類型,這時我嘗試使用v-for來對迭代器進行遍歷,固然個人指望是可以遍歷出來。 咱們都知道vue的v-for提供了兩種遍歷方式,vue

v-for="item in items"
v-for="item of items"
複製代碼

而js中能夠使用bash

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 IteratorObject.keys(val)則是一個空對象[],一個空的keys,因此此處不會產生遍歷而後進行renderui

做死的開始

既然沒有判斷出是一個Iterator那麼我就添加一個判斷,因爲剛開始覺得不能使用ES5以上的語法,修修改改了好屢次,寫出了以下的代碼,其實這裏能夠判斷類型是否爲Symbol.iteratorthis

...
  } 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.toStringTagspa

Symol.toStringTag
A string value for the default description of an Object. Used by
Object.prototype.toString()prototype

查閱資料看到了這麼一段話,那麼我就是用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類型到底是個怎樣的類型,瞭解了迭代器的實現,實踐了斷點定位問題等等...一個晚上的付出沒白費,仍是比較欣喜的。code

End

I'm 一個渴望成長的碼農orm

相關文章
相關標籤/搜索