专栏名称: 前端大全
分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯
目录
相关文章推荐
内蒙古自治区文化和旅游厅  ·  跨新年 过大年 | ... ·  昨天  
内蒙古自治区文化和旅游厅  ·  跨新年 过大年 | ... ·  昨天  
Java知音  ·  池化技术:让真实业务效率飙升的利器 ·  4 天前  
Java知音  ·  池化技术:让真实业务效率飙升的利器 ·  4 天前  
内蒙古政府办公厅  ·  内蒙古“四横十二纵”综合交通主骨架建成率达8 ... ·  4 天前  
51好读  ›  专栏  ›  前端大全

实战分享:20分钟页面不操作,页面失效

前端大全  · 公众号  ·  · 2024-06-05 11:50

正文

作者:吃腻的奶油

https://juejin.cn/post/7340636105765535796

场景需求

总结:

  1. 20分钟内如果不操作,页面就是提示失效并且回到列表页面,如果操作了,计时就会清零。
  2. 如果 A  在编辑,B 点击编辑会提示正在编辑。A 在编辑期间,每分钟会向后端发送续租(即正在编辑)的请求,后端收到请求后,会在服务端帮你保留这一分钟的编辑状态,别人就无法在编辑了。并且别人编辑时,后端会返回相应的信息。

前言

乐了,产品提出了需求,然后我去找导师问问团队中有没有现成的解决方案。。。没有,然后导师提出了 web worker 的思路,让我自己思考解决方案。好吧,那就开始吧。

一开始,我想着能否能用 setInterval 来进行定时的,结果后端发来消息

emm......后端大佬,惹不起~

如图上所说,如果切换了页面, setInterval 会停止计时的(咱就说不信的可以试试),也就是说这个线程被停止了。

那么就需要新建一个线程,也就是 web worker 了,用它单纯来进行计时,不用管其他逻辑,切换页面也不会终止。

正文思路

基本demo

首先,百度了下 web worker 的基本实现案例,一文彻底学会使用web worker

需要该需求的页面

// editEmail.vue(主线程)

放入 public 文件下 worker.js

// worker.js(worker线程)
self.addEventListener('message', e => { // 接收到消息
    console.log(e.data); // Greeting from Main.js,主线程发送的消息
    self.postMessage('Greeting from Worker.js'); // 向主线程发送消息
});

其中,worker.js 的存放路径和 new Worker()里的值有关,比如此时我是在本地资源的根路径创建的 /worker.js ,那么就是放在public下的。

而如果是 ./worker.js ,或者 ../worker.js ,这是无法找到的,因为此时的 worker.js 已经被打包编译成了 app.js。

注意,public 文件的变动需要重启项目,和 vue.config.js一样

worker.js 和 主线程通信走通后,开始分析需求了。

1. 每分钟续租一次 =》 1秒钟续租一次

什么叫续租,每分钟你向服务端发送一个续租请求,后端就会帮你保持正在编辑的状态(假设为 edit: true ),而且后端其实也在计时一分钟。在这一分钟内,由于 edit 为 true ,如果别人想要编辑,就会拒绝别人的编辑。如果你一分钟后没发送这个续租请求,后端会把 true 改成 false ,这时别人想要编辑,后端就会接受别人的编辑了。

因此,前端就需要每隔一分钟发送一次续租请求,来维持此时的编辑状态。

当然,由于产品要求的更复杂,你发送续租请求的时候请求头往往会携带用户信息,来反馈谁在进行编辑以提高用户体验感。

下述代码为了更好的测试,把每分钟续租变为了每秒续租一次

2. 20分钟期间不操作就会提示页面失效 =》 10秒钟一到就会触发提示事件

当然,就算 setInterval 不能作为解决方案,但还是需要用它来做定时器的,这还是挺香的。

// worker.js(worker线程)
self.addEventListener('message', e => { // 接收到消息
    console.log(e.data); // Greeting from Main.js,主线程发送的消息
    setInterval(() => {
        self.postMessage('Greeting from Worker.js'); // 向主线程发送消息
    }, 1 * 1000)
});

如上代码, Greeting from Worker.js 这条消息每隔 1 秒钟就会向 editEmail.vue 页面发送,这时就算你切换浏览器标签页也仍然会发送。

好,简单的定时器做完了,那就开始进行计时了。

// worker.js(worker线程)
self.addEventListener('message', e => { // 接收到消息
    console.log(e.data); // Greeting from Main.js,主线程发送的消息
    
    let sum = 0;
    let msg;
    
    setInterval(() => {
        sum += 1;
        msg = {
            text'editing',
            sum
        }
        self.postMessage(msg); // 向主线程发送消息 msg 对象
    }, 1 * 1000)
});

每过一秒,worker.js 都会发送一次信息,用来持续触发续租事件,而 sum 则是用来进行计时过了多少秒。

// editEmail.vue(主线程)

OK,这样,基本的需求就完成了, 10 秒一到就会提示页面失效,并且在这 10 秒内谁都无法进入编辑页面(在进入编辑页面前得先向后端请求看看是否有人在编辑)。

但是,10 秒后呢,这个计时器仍然在进行中,所以我需要在 10 秒过后清除这个计时器了。也就是在 e.data.sum >= 10 这个条件内对 worker 进程进行通信,触发清除事件。

// editEmail.vue(主线程)

在这里我们分别向 worker 进程发送了 start end 两个信息, worke r 进程拿到信息后进行判断,如果是 start ,那么就开始每秒续租,如果为 end ,那么就清除定时器来终止续租(即停止每秒向主线程进行通信来触发续租请求)。

// worker.js(worker线程)
let timer;
self.addEventListener('message', e => { // 接收到消息
    console.log(e.data); // Greeting from Main.js,主线程发送的消息
    
    let sum = 0;
    let msg;
    
    if (e.data === "start") {
         timer = setInterval(() => {
            sum += 1;
            msg = {
                text'编辑中',
                sum
            }
            self.postMessage(msg); // 向主线程发送消息 msg 对象
         }, 1 * 1000)
    } else {
        clearInterval(timer);
    }
});

如上代码,定义一个全局变量 timer 用来存储定时器,以便能够随时清除。

定时重置

Stop,别冲太猛,这里我们需要总结一下了

开启定时

myWorker.postMessage('start');

就会重新 worker.js 中的 self.addEventListener('message',()=>{}) 函数,sum 重置为 0,计时重新开始计算。

停止定时

myWorker.postMessage('end');

就会触发 worker 中的 clearInterval(timer) 来清除定时器

重置定时

myWorker.postMessage('end');
myWorker.postMessage('start');

先清除定时器停止定时,然后再重新开启定时

最后

// 开启定时
const onTimeStart = () => {
    myWorker.postMessage('start');
}
// 停止定时
const onTimeEnd = () => {
    myWorker.postMessage('end');
}
// 重置定时
const onTime = () => {
    onTimeEnd();
    onTimeStart();
}

3. 10 秒内如果进行了表单操作则重置计时

const onChange = () => {
    onTime();
}

优化代码

// editEmail.vue(主线程)
// worker.js(worker线程)
let timer;
self.addEventListener('message', e => { // 接收到消息
    console.log(e.data); // Greeting from Main.js,主线程发送的消息
    
    let sum = 0;
    let msg;
    
    if (e.data === "start") {
         timer = setInterval(() => {
            sum += 1;
            msg = {
                text: 'editing',
                sum
            }
            self.postMessage(msg); // 向主线程发送消息 msg 对象
         }, 1 * 1000)
    } else {
        clearInterval(timer);






请到「今天看啥」查看全文