前言

下文第一段会给出多项解决方法。后文则详细展开了后端配合的网络请求方法。
实际项目中后续改用了setInterval的不断轮询脏检查与网络请求方法结合的方式,本文内容并不是最新内容,一般应该也不会修改更新本文内容了,最重要的是下面的“所有方法”,都懂并选择自己所需要的就行。

所有方法

准备面试的时候看到了跨标签页数据传输方法的总结——

不同标签页间的通讯,本质原理就是去运用一些可以共享的中间介质,因此比较常用的有以下方法:

  1. 通过父页面window.open()和子页面postMessage(h5的新特性,可以实现简单的跨域通信)

      异步下,通过 window.open('about: blank') 和 tab.location.href = '*'
    
  2. 设置同域下共享的localStorage与监听window.onstorage(相当于浏览器端的小型数据库,但无法跨浏览器通信)

     重复写入相同的值无法触发
     会受到浏览器隐身模式等的限制
    
  3. 设置共享cookie与不断轮询脏检查(setInterval)

  4. 借助服务端或者中间层实现(我的理解是,与3一样都是需要网络请求,只不过3是用cookie来维护数据的更新,4则是具体的自定义的业务逻辑方法)

参考资料:中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)

需求

数据可视化分析。
A页面是一张表单,在此填入各种筛选条件,然后点击提交。在新标签页B页面展示结果图表。
可以在A页面更新提交表单,B页面实时更新图表结果,但不会弹出新的页面。
关闭B页面,再点击A页面,会弹出新的B页面。
可以点击侧边栏“图表分析”直接跳转到B页面,“数据选择”跳转到A页面,两个页面已经填入和已经展示的数据不能消失。
简单地说,核心需求就是,在一个页面输入表单,在另一个页面实时更新数据。

解决思路

排除的解决方法

显然,以下方法可以轻易排除:

  1. 组件通信。不能跨页面传递数据。
  2. vuex。每个页面的vuex是不通的。
  3. 路由参数。可行,但是无法实时更新数据。
要解决的问题

对需求进行抽象,总结出需要解决的若干环节,并逐一解决,具体需要解决的环节如下:

  1. 页面跳转,并打开新的页面。
  2. 记录页面数量,阻止多次打开同一页面。
  3. A页面更新数据之后,B页面能够实时更新数据。
  4. 不因离开页面而丢失A页面或者B页面已填入或展示的数据。
  5. 如果能不同浏览器数据互通最好。(所以选用了网络请求的方法)

解决办法

下文读大标题,关注一下生命周期就行。

1 页面跳转,并打开新的页面

核心代码:

window.open(
	this.$router.resolve({path: "/compcharts",}).href, '_blank'
);

用window.open方法打开页面
$router的resolve解析路由
_blank实现在新标签页打开页面
把这段代码挂载到方法中即可

2 页面计数及实时更新数据

2.1 解决思路

在后端用一个变量,记录B页面已经打开的数量。
并用一个接口,实现不同页面的数量记录的查询、增加、减少。
在打开新页面前,先查询数量,小于1则打开新页面。

A页面,只负责,发送表单数据,判断是否需要打开新页面。
B页面,只负责,获取结果数据,更新页面数量。
在A页面中,发送所有的表单数据,由后端进行存储。
在B页面中,每秒向后端发送一次请求,获取最新数据,这个最新数据是后端使用存储的最新的表单数据,调用方法函数计算出的数据。

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');
    },

由此,实现了前端在页面跳转之前的判断。
而在页面跳转之后的新页面中:

data() {
    return {
        intervalId:'',
    }
},
methods: {
	findData(){
            compApi
                .comp_catch_data()
                .then((response) => {
                    console.log("response")
                    console.log(response)
                    let dataIn = transformTableData(response[1])
                    setTimeout(() => {
                        //把数据传给父组件
                        this.outTableData =  dataIn
                    }, 300);

                }).catch((err) => {
                console.log(err);
            });
        },
        catachData(){
          this.intervalId = setInterval(() => {
            setTimeout(() => {
                ///调取接口
                this.findData()
            }, 0)
          }, 1000)
        }
}mounted(){
	window.addEventListener('unload', () => page_change(-1,'comp'))
},
activated() {
   this.catachData()
   page_change(1,'comp')
},
deactivated(){
   clearInterval(this.intervalId)
   page_change(-1,'comp')
},
beforeDestroy() {
	window.removeEventListener('unload', page_change(-1,'comp'))
},

其中的page_change是对前文那个page_count的一个封装。

两个核心问题,一个是页面数量的计数,第二个是定时器每秒发送一个请求。
要解决的关键场景是两个,第一个是在同一页面中的组件切换,第二个是新标签页的创建和关闭。

同一页面组件切换场景,keep-alive的情况下,会触发activated和deactivated生命周期函数。即,将组件丢进缓存和把组件从缓存里取出来的时候会触发。以此来进行页面计数、定时器开启与关闭。
新标签也的创建和关闭场景,需要监控标签页window,mounted挂载时要开始对页面的unload事件进行监控,页面关闭组件销毁前要把监控给关闭掉。而对于定时器,随着页面的打开,activated也会触发,随着页面关闭,一切都销毁了,更遑论定时器。

选用合适的生命周期函数和浏览器事件,是此中关键。

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,返回的是更新后的数据。查询不会更改数量。

至于数据的实时更新。
就是一个接口,响应“comp_filter”,并像上文pagecount一样,更新一个保存请求参数数据的变量compquest
再加一个接口,响应“comp_catch_data”接口,根据保存的请求参数数据的变量,调用业务方法,计算出需要的数据,返回给前端即可。
业务代码就不放了。

用全局变量是行之有效的笨办法,有条件的朋友最好改成更好的暂存方式,比如缓存之类的。目前版本是不支持也不需要支持多用户的,要改成支持并不麻烦但无必要。

3 缓存页面数据

keep-alive即可实现A页面去了别的页面之后,回来数据还在。
而因为后台保存了最新提交的一次表单的数据,所以B页面只要不停发送请求,就能由最新的表单数据获得所需的制图数据。
值得注意的是,后端的这两个接口要分开设计,不能合并成一个接口,否则在业务上、代码上,都会增加不小的复杂性,增加出各种bug的可能性,增加不同需求矛盾的可能性,并且使代码显得冗长、杂乱、不干净。

总结

并不是页面数据之间的传递,因为这无法实现。
而是将后端作为中转站,一个页面将重要数据发给后端暂存。
另一个页面发出请求,获取由后端暂存的实时数据计算出的结果。
从而实现了A页面数据更新,B页面实时更新,这样的多页面数据传输的效果。

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐