业务说明
现在公司有这么一项业务需要:一个表单有多个输入框,每个输入框可以设置公式,由别的输入框的值求得。
思路:每个输入框绑定watch事件。
实现
我们能从表单数据中拿到每个输入框里面的信息,包括name,数据,公式,涉及到的输入框。
通过遍历当前输入框的涉及到的输入框,生成一个map,key是涉及到的输入框的name,如果该key存在,则将当前输入框的name push进去;如果不存在,则新建键值对。
// relyMap是要生成的map,sname是当前输入框的name,map是当前输入框涉及的输入框
let relyMap={},sname
for (let j in Map) {
if (relyMap[j]) {
relyMap[j].push(sname)
} else {
relyMap[j] = [sname]
}
}
遍历生成的relymap,对于每个键都用watch绑定,当被绑定监听的对象值发生改变时,遍历上方生成的数组,将每个键对应的公式以及对应的数据结构传给专门负责计算的函数,将返回值通过set将值绑定
this.$watch('name.' + i, function (newVal, oldVal){
relymap[i].foreach(j => {
// 求和
// set赋值
})
})
算法
很简单是不是,但是如果a=b+c 的情况下,对于bc在新加公式b=a+c和c=a+b后,这就成了一个环,我们修改a的话就会触发b和c的改变,bc改变后又会触发ac,ab的改变,然后又会相互触发,不上锁的话会在瞬间吃爆内存,同时这些都是在watch内部发生的,我们没办法直接查看修改watch里面的数据。
因此设计了一个bfs和dfs合并的保护算法,逻辑为:
- 当被绑定监听的值被修改后,如果广搜的数组为空,代表时用户触发了监听,此时直接将该键关联的数据直接推入到广搜的数组中,如果不为空代表是关联触发的,不用管。
- 遍历该键关联的全部字段,对于每个字段:
- 到这一步代表已经触发监听了,监听数量的变量+1
- 判断dfs数组里面是否有当前字段,如果有代表已经操作过了,直接return
- 将当前字段推入深搜数组里面
- 将该字段所关联的字段推入广搜数组
- 通过函数计算值
- 通过set赋值
- 判断当前监听变量是否与bfs长度相等,如果相等代表我触发的watch和watch队列的里面是一样的,清空对应的值
let dfs=[]
let bfs=[]
let watch=0
for(let i in relymap) {
this.$watch('name.' + i, function (newVal, oldVal){
relymap[i].foreach(j => {
if (bfs.length === 0) bfs.push(...relymap[i])
relymap[i].forEach(j => {
watch++
if (dfs.indexOf(j) !== -1) return
bfs.push(...(relyMap[j] || []))
dfs.push(j)
// 求和
// set赋值
})
if (watch === bfs.length) {
dfs = []
bfs = []
watch = 0
}
})
})
}
总结
因为watch不能直接操作,所以实现了一种在监听外面贴了一层算法,通过对算法的值进行计算来判断是否真的结束了全部的监听