这次在写业务的时候因为是无限层级的递归表单,用了 provide/inject
来暴露注册以便收集数据。所以就想要用 Map
数据结构来处理数据的收集等等功能。直接使用 记录ID 作为键名就可以了,这样也可以直接去重。但是写完了业务代码之后发现我把后代的表单注册进来并没有触发响应,导致数据回填失败。
所以想着是不是 Vue 2x
无法监听 Map
或者 Set
的数据变更的。就去检索了一下相关的信息,发现 Vue 2x
真的无法监听 Map
以及 Set
俩兄弟。只能够通过修改其他变量来曲线救国。
Vue 3x
通过 Proxy
来代理就没有了这个问题。
想要在 Vue2
中来实现我上面的需求的话:
#方法1. 改写成 数组 对象。每次去 find
一下是否已经存在上级元素,然后插入到 child
中,但是对于层级比较深的就不是特别好使了,需要递归才行。
所以我先 const list = []
声明了一个数组对象,同时也 const tmpMapper = new Map()
声明一个 Map
数据对象把这些 一级祖先 .set
进去,因为是同一个引用所以 数组内的数据 和 Map
内的数是同一个,直接修改 Map
内的属性 数组内的元素也会变更。
然后开始遍历通过 parentId
关联的平级结构列表,因为是从前往后正序排列的,所以直接通过 tmpMapper.get(item.parentId)
就可以获取到父级了,然后 push
当前元素到父级的 child
当中,同时也 .set
到 tmpMapper
当中。如此往复,这样就可以把通过 parentId
关联的平级结构列表转成树结构了。最后再把 list
赋值给在 data
中提前声明好的数组变量就可以了。
只是后续操作修改的时候都会比较麻烦。不过好在我只有初始化的时候需要这样创建一次就行了。之后通过转换成树状的数组递归渲染即可。
#方法2. 添加一个标识符,在每次操作 Map
对象的时候去给这个标识符 +1
,这样通过监听数字标识符的变更来触发更新。
一般来说这样都是通过 computed
来把 Map
对象转为数组。因为其实最终在 template
中循环的时候其实也是一个可递归的变量,所以使用 Array.form()
直接转换成数组使用就行了。
data() {
mySetChangeTracker: 1,
mySet: new Set(),
},
computed: {
mySetAsList() {
// By using `mySetChangeTracker` we tell Vue that this property depends on it,
// so it gets re-evaluated whenever `mySetChangeTracker` changes
return this.mySetChangeTracker && Array.from(this.mySet);
},
},
methods: {
add(item) {
this.mySet.add(item);
// Trigger Vue updates
this.mySetChangeTracker += 1;
}
}
相关阅读
[Feature]: Add support of the iterator protocol (es6) for v-for directive (map, weakset, weakmap) · Issue #1319 · vuejs/vue
vuejs doesn’t support Sets as backing fields for binding (nor maps, weakmap, weakset reactivity) · Issue #5241 · vuejs/vue
Support more collection data types in v-for · Issue #2410 · vuejs/vue