前言
需求
数据可视化分析。
A页面是一张表单,在此填入各种筛选条件,然后点击提交。在新标签页B页面展示结果图表。
可以在A页面更新提交表单,B页面实时更新图表结果,但不会弹出新的页面。
关闭B页面,再点击A页面,会弹出新的B页面。
可以点击侧边栏“图表分析”直接跳转到B页面,“数据选择”跳转到A页面,两个页面已经填入和已经展示的数据不能消失。
简单地说,核心需求就是,在一个页面输入表单,在另一个页面实时更新数据。
解决思路
排除的解决方法
vue是单页面开发框架,也就是说,无法通过框架内部的方法来进行数据的传输(至少我没找到),因此有一下几种方法是无效或者不可行的:
- 组件通信。不能跨页面传递数据。
- vuex。每个页面的vuex是不通的。
- 路由参数。可行,但是无法实时更新数据。
要解决的问题
对需求进行抽象,总结出需要解决的若干环节,并逐一解决,具体需要解决的环节如下:
- 页面跳转,并打开新的页面。
- 记录页面数量,阻止多次打开同一页面。
- A页面更新数据之后,B页面能够实时更新数据。
- 不因离开页面而丢失A页面或者B页面已填入或展示的数据。
解决办法
1 页面跳转,并打开新的页面
核心代码:
window.open(
this.$router.resolve({
path: "/compcharts",
}).href,'_blank');
用window.open方法打开页面
$router的resolve解析路由
_blank实现在新标签页打开页面
把这段代码挂载到方法中即可
this.$router不能分离到utils文件夹下的.js文件中使用,只能在main.js声明了全局变量之后的.vue中使用,所以我暂时没能把其完全抽象剥离,后续如果花时间解决了再来更新此处博客,或者有朋友知道的话欢迎评论区评论,不胜感激。
2 记录页面数量,阻止多次打开同一页面
2.1 解决思路
在后端用一个变量,记录某页面已经打开的数量。
并用一个接口,实现不同页面的数量记录的查询、增加、减少。
在打开新页面前,先查询数量,小于1则打开新页面。
打开新页面后,在组件创建时调用接口增加数量(后续可能需要修改为页面打开,如修改了会来更新此处文章),在页面关闭时减少数量,在页面刷新时会在生命过程中使用上述两个方法所以数量不变。
2.2 前端
页面跳转方法:
jumppage() {
commonApi
.page_count(0, 'comp')
.then((res) => {
console.log(res)
if (res < 1) {
pagefunc
.open_new_window(this.$router.resolve({
path: "/compcharts",
}))
} else {
console.log("页面已经打开")
}
}).catch((err) => {
console.log(err)
})
},
其中的page_count是:
page_count(changenum,changepage){
console.log("page_count")
return request({
url: config.url.common.pageCount,
method: 'post',
data: {
changenum: changenum,
changepage: changepage
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
},
而pagefunc中的open_new_window其实就是上文页面跳转的一个简单封装罢了:
open_new_window(chartsData) {
window.open(chartsData.href, '_blank');
},
由此,实现了前端在页面跳转之前的判断。
而在页面跳转之后的新页面中:
需要创建一个方法:
unloadHandler(){
commonApi
.page_count(-1, 'comp')
.then((res) => {
console.log(res)
}).catch((err) => {
console.log(err)
})
}
如果是增加数量则是把"-1"改成1即可,放在created中(可能需要改到页面的生命周期中,而不是vue的生命周期中,完成修改后更新本文)
然后,在mounted中挂载:
window.addEventListener('unload', () => this.unloadHandler())
最后,在beforeDestroy中卸载:
window.removeEventListener('unload', this.unloadHandler())
值得注意的是,你不能直接在vue的生命周期函数中使用方法,因为准确地触发事件的并不是组件的生命,而是这个标签页的生命。如果绑在了组件上,那么由于B页面的访问方式既有新标签页访问,又有单页面方式访问,所以组件的生命周期会出现Bug,需要再做分析处理。
(目前销毁的我已经改好了,但是新建的那个+1的计数绑在了created里,已经出现单页面组件keep-alive,未销毁组件的bug了,所以前文多次提到需要修改)
重中之重,要分析清楚触发事件的关键环节是组件的生命周期,还是浏览器页面的生命周期,他们是 不 一 样 的 。
2.3 后端
建一个变量,进行存储:
pagecount = {
'test': 0,
'comp': 0,
'freq': 0,
'quie': 0
}
返回更新后的数据:
class PageCountHandler(BaseHandler):
def post(self, *args, **kwargs):
changenum = self.get_argument('changenum')
changepage = self.get_argument('changepage')
global pagecount
pagecount[changepage] = pagecount[changepage] + int(changenum)
json_str = json.dumps(pagecount[changepage])
self.write(json_str)
对于查询的,changenum=0即可,增加是1,减少是-1,返回的是更新后的数据。查询不会更改数量。
查询应当放在创建新页面之前(A页面),而对页面数量的增减应当放在新页面的生命周期中(B页面),不能在创建的同时进行增减。因为正如上文所言,行为应与事件紧密相关,如果放置在A页面查询之后,会引起与B页面数量增减的在逻辑上的不清晰乃至混乱,是更加不优雅难看的代码,也是不当的。
3 A页面更新数据之后,B页面能够实时更新数据
3.1 解决思路
A页面,只负责,发送表单数据,判断是否需要打开新页面。
B页面,只负责,获取结果数据,更新页面数量。
因此,在A页面中,发送所有的表单数据,由后端进行存储。
在B页面中,每秒向后端发送一次请求,获取最新数据,这个最新数据是后端使用存储的最新的表单数据,调用方法函数计算出的数据。
即,A页面,发送数据,更新后端的表单;B页面,发送请求,后端由存储的表单计算出图表数据,发给B页面。
3.2 要注意的点
为什么说这篇文章的解决办法有点笨呢,就是因为这个B页面每秒发一次请求的方式有点笨。
但是用vue这种单页面框架,A页面不能给B页面直接多页面跨页面地发送或者共享数据,B页面也无法不用网络请求就获取不同的(即实时的)后端计算出的数据,所以就目前我的技术而言便只能使用这一种笨笨的办法了。
值得注意的是,记得销毁计时器,否则单页面中切换组件,不会销毁组件,计时器就不会自动销毁,大量的定时请求计时器,就会把内存爆掉,也会把后端给爆掉。
这部分代码没其实没什么东西了
3.3 前端
首先,在A页面,就是调用一个“comp_filter
”接口,发送表单数据。
业务代码不放了,就是个普通接口。
然后,在B页面,就是调用一个“comp_catch_data
”接口,发送请求。然后使用定时器就好了。
window.setInterval(() => {
setTimeout(() => {
//调取接口
}, 0)
}, 1000)
记得销毁定时器
3.4 后端
业务代码就不放了。
就是一个接口,响应“comp_filter
”,并像上文一样,更新一个保存请求参数数据的变量。
再加一个接口,响应“comp_catch_data
”接口,根据保存的请求参数数据的变量,调用业务方法,计算出需要的数据,返回给前端即可。
4 不因离开页面而丢失A页面或者B页面已填入或展示的数据
用keep-alive
即可实现A页面去了别的页面之后,回来数据还在。
而因为后台保存了最新提交的一次表单的数据,所以B页面只要不停发送请求,就能由最新的表单数据获得所需的制图数据。
值得注意的是,后端的这两个接口要分开设计,不能合并成一个接口,否则在业务上、代码上,都会增加不小的复杂性,增加出各种bug的可能性,增加不同需求矛盾的可能性,并且使代码显得冗长、杂乱、不干净。
总结
并不是页面数据之间的传递,因为这无法实现。
而是将后端作为中转站,一个页面将重要数据发给后端暂存。
另一个页面发出请求,获取由后端暂存的实时数据计算出的结果。
从而实现了A页面数据更新,B页面实时更新,这样的多页面数据传输的效果。