vue页面实现前进刷新,后退不刷新

背景

业务需求,实现跳转进页面的时候重新加载页面,后退的时候保持缓存。搜索了很多的回答,大概有几种方法:
1、keepalive判断router-view --> 缓存不生效。
2、includes --> 无法实现前进刷新
3、页面里做路由监听 --> 没有测试,太过于繁琐

方案

1、重写router的push和replace方法,记录路由的历史
2、跳转的方法里加入t参数,清除路由缓存,实现刷新
3、跳转回已有路由时,清除后面的记录,不会无限返回; exp a -> b -> c -> d, d跳转到b时,再返回时回到a

// App.vue
<keep-alive>
    <router-view ref="routerView" :key="$route.fullPath"></router-view>
</keep-alive>


// router/index.js
Vue.use(VueRouter);
let historyList = [];
let checkFlag = false; // 判断路由记录返回标识
let previousPath = ''; // 前一个路由  用于返回后重定向
let targetPath = ''; //

router.afterEach(to => {
    if (!historyList.includes(to.path) && !to.query.replace) historyList.push(to.path);

    if (to.fullPath === previousPath) {
        router.push(targetPath);

        previousPath = '';
        targetPath = '';
    }

    console.log('route each histroy--', JSON.stringify(historyList));
});

// 监听返回事件
window.addEventListener('popstate', () => {
    console.log('popstate begin', checkFlag);
    !checkFlag && historyList.pop();
    checkFlag = false;
    console.log('popstate end', JSON.stringify(historyList));
});


function checkRoute(location) {
    const index = historyList.findIndex(path => path === location.path);
    if (index !== -1) {
        console.log('index--', index);
        const backLen = index - historyList.length;
        previousPath = historyList[index - 1];
        historyList = historyList.slice(0, index);
        checkFlag = true;
        router.go(backLen);
        console.log('1234 route back ---- backlength==', backLen);
    return false;
    }
    return true;
}

const originalPush = VueRouter.prototype.push;
const originalReplace = VueRouter.prototype.replace;
// 重写原型上的push和replace方法,统一处理跳转和错误捕获
VueRouter.prototype.push = function (target) {
    let location = target;
    if (typeof target === 'string') {
        const path = target.split('?')[0];
        const query = parseQuery(target.split('?')?.[1] ?? '');
        location = { path, query };
    }

    const result = checkRoute(location);

    if (result) {
        location.query = Object.assign({}, location.query, {
            t: new Date().getTime()
        });

        console.log('push begain');

        return originalPush.call(this, location).catch(err => console.log(err));
    }
};

VueRouter.prototype.replace = function (target) {
    let location = target;
    if (typeof target === 'string') {
        const path = target.split('?')[0];
        const query = parseQuery(target.split('?')?.[1] ?? '');
        location = { path, query };
    }

    const result = checkRoute(location);

    if (result) {
        location.query = Object.assign({}, location.query, {
            t: new Date().getTime(),
            replace: true
        });

        console.log('push begain');

        return originalReplace.call(this, location).catch(err => console.log(err));
    }
};

2023/2/21更新

v3版本有所不同,代码如下

// APP.vue
<template lang="pug">
div
    router-view(v-slot='{ Component }')
        keep-alive
            component(:is='Component', :key='route.fullPath')
</template>

<script lang="ts">
    import { defineComponent } from 'vue';
    import { useRoute } from 'vue-router';

    export default defineComponent({
        setup() {
            const route = useRoute();

            return {
                route
            };
        }
    });
</script>

// 路由router.ts
import { Router, RouteLocationNormalized, parseQuery } from 'vue-router';

export default function intercept(router: Router) {
    let historyList: Array<string> = [];
    let routeReplace = false; // replace方法不添加历史
    let checkFlag = false; // 判断路由记录返回标识

    router.afterEach((to: RouteLocationNormalized) => {
        window.scrollTo(0, 0);
        document.title = String(to.meta?.title);

        if (!historyList.includes(to.path) && routeReplace === false) historyList.push(to.path);

        console.log('after each routerHistory===', JSON.parse(JSON.stringify(historyList)));
    });

    window.addEventListener('popstate', () => {
        console.log('window popstate begain', window.history, historyList.length);

        !checkFlag && historyList.pop();

        checkFlag = false;

        console.log('widow popstate routerHistory===', JSON.parse(JSON.stringify(historyList)));
    });

    function checkRoute(location: any) {
        const index = historyList.findIndex(path => path === location.path);
        console.log('index===', index);

        if (index !== -1) {
            const backLen = index + 1 - historyList.length;
            historyList = historyList.slice(0, index);
            console.log('histroy lsit', JSON.parse(JSON.stringify(historyList)));
            checkFlag = true;
            router.go(backLen);
        }
    }

    const originalPush = router.push;
    const originalReplace = router.replace;
    // 重写原型上的push和replace方法,统一处理跳转和错误捕获
    router.push = function push(target: any) {
        let location: any = target;
        if (typeof target === 'string') {
            console.log(target.split('?'));

            const path = target.split('?')[0];
            const query = parseQuery(target.split('?')?.[1] ?? '');
            location = { path, query };
        }

        console.log('push location----', location);
        routeReplace = false;
        checkRoute(location);

        location.query = Object.assign({}, location.query, {
            t: new Date().getTime()
        });
        return originalPush.call(this, location).catch(err => console.log(err));
    };

    router.replace = function replace(target: any) {
        let location: any = target;
        if (typeof target === 'string') {
            const path = target.split('?')[0];
            const query = parseQuery(target.split('?')?.[1] ?? '');
            location = { path, query };
        }

        console.log('replace location----', location);
        routeReplace = true;
        checkRoute(location);

        location.query = Object.assign({}, location.query, {
            t: new Date().getTime()
        });

        return originalReplace.call(this, location).catch(err => console.log(err));
    };

    return router;
}

2023/7/28更新
上述v3版本的代码在某些情况下会出现错误,而且定时器跳转在体验上不太友好,因此我在这段代码的基础上做了一些修改,目前测试下来没有问题,完整的代码如下

export default function intercept(router: Router) {
let historyList: Array<string> = [];
  let checkFlag = false; // 判断路由记录返回标识

  router.beforeEach(
    (
      to: RouteLocationNormalized,
      from: RouteLocationNormalized,
      next: NavigationGuardNext
    ) => {
      if (checkFlag) {
        checkFlag = false;

        next({
          ...to,
          replace: true,
          query: { ...to.query, t: new Date().getTime() },
        });
      } else {
        next();
      }
    }
  );

  router.afterEach(
    (to: RouteLocationNormalized, from: RouteLocationNormalized) => {
      window.scrollTo(0, 0);
      document.title = String(to.meta?.title);

      traceViewManual();

      if (!historyList.includes(to.path) && !to.query.replace)
        historyList.push(to.path);

      if (
        from.path === historyList[historyList.length - 1] &&
        to.path === historyList[historyList.length - 2]
      ) {
        historyList.pop();
      }

      console.log(
        'after each routerHistory===',
        JSON.parse(JSON.stringify(historyList))
      );
    }
  );

  window.addEventListener('popstate', () => { });

  function checkRoute(location: any) {
    const index = historyList.findIndex((path) => path === location.path);

    // tab页不处理
    if (index !== -1) {
      const backLen = index - (historyList.length - 1);
      historyList = historyList.slice(0, index + 1);
      checkFlag = true;
      router.go(backLen);
      return false;
    }

    return true;
  }

  const originalPush = router.push;
  const originalReplace = router.replace;
  // 重写原型上的push和replace方法,统一处理跳转和错误捕获
  router.push = function push(target: any) {
    let location: any = target;
    if (typeof target === 'string') {
      const path = target.split('?')[0];
      const query = parseQuery(target.split('?')?.[1] ?? '');
      location = { path, query };
    }

    const result = checkRoute(location);

    if (result) {
      location.query = { ...location.query, t: new Date().getTime() };

      return originalPush
        .call(this, location)
        .catch((err: any) => console.log(err));
    }
    return Promise.resolve(undefined);
  };

  router.replace = function replace(target: any) {
    let location: any = target;
    if (typeof target === 'string') {
      const path = target.split('?')[0];
      const query = parseQuery(target.split('?')?.[1] ?? '');
      location = { path, query };
    }

    const result = checkRoute(location);

    if (result) {
      location.query = {
        ...location.query,
        t: new Date().getTime(),
        replace: true,
      };

      console.log('replace begain');

      return originalReplace
        .call(this, location)
        .catch((err: any) => console.log(err));
    }
    return Promise.resolve(undefined);
  };

  return router;
}

TIP

后续如果有问题继续更新。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容