结论
Vue 中对于遍历一个对象,如果该对象有实现自己的Symbol.iterator方法的话,会调用该方法来进行遍历得到结果。否则的话会按照 Object.keys 的顺序来进行遍历。
由于使用 Object.keys 遍历出来的结果是无序的,如果想保证对象的遍历顺序,则需要自己实现 Symbol.iterator 方法来控制每次调用 next 方法返回的都是什么值
拓展
1)v-for 中处理对象的循环是怎么样的?
我们在这里还是先用一个案例来解决这个问题:
const compiler = require('vue-template-compiler')
const res = compiler.compile(`<div class='root-container'>
<div v-for="(item, index) in list" :key="index">
渲染元素{{index}}
</div>
</div>`)
执行完上述代码后,我们会得到一个结果对象,我们只需要关注里面的 render 方法即可。
下面就是 render 函数的结果:
with (this) {
return _c('div', {
staticClass: "root-container"
}, _l((list), function (item, index) {
return _c('div', { key: index }, [_v("\n 渲染元素" + _s(index) + "\n ")])
}), 0)
}
相关方法解析:
_c: 创建 VNode 节点
_v: 创建文本节点
_l:处理列表数据
_s:Object.prototype.toString 方法
我们只需要关注,_l 这个方法的内部实现即可。由于内部还有其他类型的处理,这里我们只讨论对象遍历,所以这里就只贴对对象的处理的逻辑。
// 这里的 val 就是传进去的遍历 list
if (isObject(val)) {
// 如果当前支持 Symbol,并且 val 拥有 Symbol.iterator 方法
if (hasSymbol && val[Symbol.iterator]) {
ret = []
const iterator: Iterator<any> = val[Symbol.iterator]()
let result = iterator.next()
// 循环执行 val 迭代器的 next 方法
// 直到完毕
while (!result.done) {
ret.push(render(result.value, ret.length))
result = iterator.next()
}
} else { // val 没有 Symbol.iterator 方法
// 这里直接用 Object.keys 获取对象的 key 值去进行遍历
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)
}
}
}
return ret
那到这里的话,其实已经证明了:如果该对象有实现自己的Symbol.iterator方法的话,会调用该方法来进行遍历得到结果。否则的话会按照 Object.keys 的顺序来进行遍历。
2)实现自己控制对象遍历顺序
首先来看正常的对象遍历顺序:
<template>
<div>
<div v-for="(item, index) in renderedObject" :key="index">
{{ item }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const renderedObject = ref({
name: 'zs',
age: '30',
height: 190
})
</script>
执行完是这样:
也就是现在的遍历顺序是:name、age、height
然后我们现在为该对象实现一个Symbol.iterator方法来让对象按自定义顺序渲染
<template>
<div>
<div v-for="(item, index) in renderedObject" :key="index">
{{ item }}
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const originalObject = {
name: 'zs',
age: '30',
height: 190
}
const renderedObject = ref({
[Symbol.iterator]() {
// 闭包索引,记录调用的累计次数
let nextIndex = 0
// 控制渲染的对象 key 映射
let keyMap = {
0: 'height',
1: 'age',
2: 'name',
}
let len = Object.keys(originalObject).length
return {
next() {
// 索引小于长度之前一直去取对象中的属性
if (nextIndex < len) {
const key = keyMap[nextIndex++]
const res = originalObject[key]
return {
done: false,
// 返回对应结果
value: res
}
}
return {
done: true,
value: undefined
}
}
}
}
})
</script>
我们再来看看输出结果:
现在的遍历顺序是:height、age、name
至此,我们就实现了对象自定义渲染顺序,如果有帮助到你,请帮忙点个👍🏻。